Skip to content

Middleware

intermediate2 min read
  • Express
  • Node.js

Functions that sit in the request pipeline and run before route handlers, used for cross-cutting concerns like parsing, authentication, logging, and errors.

Prerequisites

  • Express basics and the request-response pipeline (see Related Topics)

What you'll understand

  • What middleware is and where it runs in the request pipeline
  • How the next function passes control along the chain
  • Why middleware is the right home for cross-cutting concerns

Explanation

Middleware is a function that runs in the request pipeline before (or instead of) a route handler. It receives the request, the response, and a next function. It can inspect or modify the request, short-circuit by sending a response, or call next() to pass control to the next function in the chain. A route handler is really just the last middleware, the one that sends the response.

The power of middleware is that it factors out cross-cutting concerns, work that many routes need but that is not specific to any one of them. Parsing JSON bodies, authenticating a token, logging each request, and enforcing rate limits are all classic middleware. Instead of repeating that code in every handler, you write it once and place it in the pipeline.

Order is everything. Middleware runs in the order it is registered, so authentication must come before the routes it protects, and a body parser must come before handlers that read the body. Error-handling middleware is special: it takes four arguments (err, req, res, next) and runs only when something upstream passes an error, which makes it the natural place to centralise error responses (see Related Topics).

Examples

This TypeScript example is a simple logging middleware that records each request and then calls next():

import type { Request, Response, NextFunction } from 'express';

function requestLogger(req: Request, res: Response, next: NextFunction) {
  console.log(`${req.method} ${req.path}`);
  next(); // pass control to the next function in the pipeline
}

app.use(requestLogger);

This example short-circuits the pipeline when a request is not authenticated, so the route never runs:

function requireAuth(req: Request, res: Response, next: NextFunction) {
  if (!req.headers.authorization) {
    return res.status(401).json({ message: 'Unauthorized' });
  }
  next();
}

Common mistakes

  • Forgetting to call next() (and not sending a response), which leaves the request hanging.
  • Calling next() after already sending a response, causing a "headers already sent" error.
  • Registering middleware in the wrong order, so it runs after the routes it should protect.
  • Writing an error handler with three parameters; Express only treats a four-argument function as an error handler.

Best practices

  • Use middleware for cross-cutting concerns rather than repeating logic in each handler.
  • Register global middleware before your routes, and route-specific middleware on the route.
  • Either send a response or call next() in every middleware, never both.
  • Keep a single central error-handling middleware for consistent failure responses.

Further reading

Related topics

Middleware — Padlor · Padlor