Glossary

Middleware

Middleware is a function that sits between receiving an HTTP request and sending a response, transforming requests, enforcing policies, or adding data to the request context. Multiple middleware functions form a pipeline.

Explanation

In Express.js (and similar frameworks like Koa, Hapi, and Django), middleware functions have access to the request object (req), the response object (res), and a next() function that passes control to the next middleware in the chain. An incoming HTTP request flows through each middleware in order until one sends a response or an error occurs. Common middleware patterns: authentication (verify a JWT or session before allowing access to protected routes), logging (record request details, timing, and status codes), rate limiting (reject requests beyond a threshold), input validation and parsing (parse JSON bodies, validate schemas), compression (gzip responses), CORS headers (add Cross-Origin headers), error handling (catch errors thrown in any preceding middleware), and request enrichment (add user data to req.user for downstream use). Order matters: middleware runs in the order it's registered. Authentication middleware must come before route handlers that need authentication. Error-handling middleware (4-argument functions: err, req, res, next) must be registered after route handlers to catch errors they throw. Body parsers (express.json()) must be registered before any route that reads req.body. The middleware pattern (also called "pipeline" or "chain of responsibility") is not unique to web frameworks. Database query builders, Redux reducers with enhancers, build tools (webpack loaders), and HTTP client interceptors (Axios interceptors) all use the same pattern: transform data as it passes through a chain of functions.

Code Example

javascript
// Express middleware pipeline
const express = require('express');
const app = express();

// 1. Parse JSON bodies (must come first)
app.use(express.json());

// 2. Log every request
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    console.log(`${req.method} ${req.path} ${res.statusCode} ${Date.now()-start}ms`);
  });
  next(); // MUST call next() or request hangs
});

// 3. Authentication middleware
function requireAuth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'Unauthorized' });
  try {
    req.user = verifyToken(token); // attach user to request
    next();
  } catch {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// 4. Route: open (no auth)
app.get('/public', (req, res) => res.json({ message: 'Hello world' }));

// 5. Route: protected (requireAuth middleware applied)
app.get('/profile', requireAuth, (req, res) => {
  res.json({ user: req.user });
});

// 6. Global error handler (must have 4 params)
app.use((err, req, res, next) => {
  console.error(err);
  res.status(err.status ?? 500).json({ error: err.message });
});

Why It Matters for Engineers

Middleware is the architecture pattern behind every Node.js API and many other frameworks. Understanding it means understanding how authentication, logging, validation, and error handling are composed — and why forgetting next() causes requests to hang forever, or why registering middleware in the wrong order silently breaks auth. The middleware pattern also generalizes to many other contexts: understanding it gives you a mental model for interceptors, plugins, and pipelines throughout software engineering. Redux middleware, Webpack loaders, database query hooks — all the same pattern.

Related Terms

REST API · HTTP · CORS · Session

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Web Development Fundamentals → →