Skip to content

Express Middleware

You already understand the middleware pattern conceptually. This lesson shows exactly how Express implements it, application-level, router-level, and built-in middleware, and how they compose.

Middleware in Express

In Express, middleware is any function with the signature (req, res, next) registered via app.use() or a route method. It runs for every matching request, in the order it was registered, and either calls next() to continue the pipeline or sends a response itself.

Express ships several built-in middleware functions, most importantly express.json() for parsing JSON bodies and express.static() for serving files, and the ecosystem provides thousands more as npm packages (cors, helmet, morgan, and others covered later in this course).

import express from 'express';

const app = express();

app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

app.use(express.json());

app.post('/orders', (req, res) => {
  res.status(201).json({ received: req.body });
});

The logging middleware runs first for every request, then express.json() parses the body before the route handler can read req.body.

Middleware Registration Patterns

app.use(middleware);                    // applies to every request
app.use('/admin', middleware);           // applies only under /admin
app.get('/path', middleware, handler);   // route-specific middleware
router.use(middleware);                  // router-level middleware
  • app.use(fn) with no path applies to every method and every path.
  • app.use(path, fn) scopes middleware to requests under that path prefix.
  • Route handlers can accept multiple middleware functions before the final handler.
  • router.use() scopes middleware to only the routes defined on that specific router.

Express Middleware Cheatsheet

Common built-in and third-party middleware you'll use constantly.

Middleware Purpose
express.json() Parses JSON request bodies into req.body
express.urlencoded() Parses form-encoded bodies
express.static('public') Serves static files from a directory
cors() Enables Cross-Origin Resource Sharing (third-party)
helmet() Sets security-related HTTP headers (third-party)
morgan('dev') Logs incoming requests (third-party)

How the Middleware Pipeline Executes

Express walks through registered middleware and routes in the exact order they were added to the app, calling each one's next() to advance. Middleware needed by later steps, like body parsing before a route reads req.body, must be registered earlier in the file.

app.use(express.json());   // 1. parse body first
app.use(requireApiKey);     // 2. then check auth (can read parsed body if needed)

app.post('/orders', createOrder);  // 3. route runs last

Writing Your Own Middleware

Custom middleware is just a regular function, the same (req, res, next) signature you built by hand earlier in this course, now plugged into Express's pipeline instead of a hand-rolled one.

function requestTimer(req, res, next) {
  req.startTime = Date.now();
  next();
}

function logDuration(req, res, next) {
  res.on('finish', () => {
    console.log(`${req.method} ${req.url} took ${Date.now() - req.startTime}ms`);
  });
  next();
}

app.use(requestTimer, logDuration);

Common Mistakes

  • Registering body-parsing middleware after the routes that need req.body, leaving it undefined.
  • Forgetting to call next() inside custom middleware, silently stalling the request.
  • Calling next() after already sending a response, triggering "headers already sent" errors.
  • Registering security middleware like helmet()/cors() too late, after routes have already run.

Key Takeaways

  • Express middleware shares the same (req, res, next) signature you learned in the middleware basics lesson.
  • app.use() registers middleware for all requests or a specific path prefix.
  • Middleware order matters, dependencies (like body parsing) must be registered before code that needs them.
  • Built-in and third-party middleware cover most cross-cutting concerns without custom code.

Pro Tip

Group your middleware registration into a predictable order at the top of your app file: security first (helmet, cors), then logging, then body parsing, then your own auth checks, then routes. A consistent order across projects makes debugging pipeline issues much faster.