Middleware: logging and error handling

Middleware is a function that sits between the request coming in and your route handler. Authentication, logging, request IDs, rate limiting, error handling. Anything that has to apply across many routes belongs as middleware.

after/middlewares/logger.ts
typescript
import { Request, Response, NextFunction } from 'express';
import winston from 'winston';

export const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json(),
  ),
  transports: [new winston.transports.Console()],
});

export function requestLogger(req: Request, res: Response, next: NextFunction) {
  logger.info(req.method + ' ' + req.originalUrl);
  next(); // do not forget this
}

export function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
  logger.error(err.message || 'Internal Server Error');
  res.status(err.status || 500).json({
    error: err.message || 'Something went wrong',
  });
}

Logger and error handler middleware. The logger uses Winston so logs are structured.

The single most common bug with middleware: forgetting to call next(). Without it, the request hangs forever, the client times out, and you wonder why your server stopped responding. Make next() the muscle memory: every middleware call ends with it unless you have explicitly sent a response.

Express identifies error-handling middleware by counting parameters. Four parameters (err, req, res, next) tells Express this function is for handling errors. Even if you do not use next, it has to be in the signature. It is one of those quirks you only need to learn once.

after/index.ts
typescript
import express from 'express';
import { bookRouter } from './routes/book.routes';
import { requestLogger, errorHandler } from './middlewares/logger';

export const app = express();

app.use(express.json());
app.use(requestLogger);                  // runs for every request
app.use('/api/books', bookRouter);

app.use(errorHandler);                   // catches errors from any handler

Wire the middleware into the app. Order matters: logger first, error handler last.

Quiz: Quiz

Loading practiceโ€ฆ

Validation checklist: Build the layered API

Loading practiceโ€ฆ

Your in-memory array works perfectly until you restart the server. Then everything you added is gone. Next up you replace the array with a real Postgres database, and you see the payoff of the layers you just built: only the data and service files change. Routes and controllers do not move at all.

Checkpoint: REST APIs with Express checkpoint

Loading practiceโ€ฆ