Chapter 48 โ€” Middleware

๐Ÿ“– Definition

Middleware is a function that has access to the request, the response, and a next callback in the application's request-response cycle. It can perform work, modify req/res, send a response, or pass control onward.

๐Ÿ” Explanation

Express middleware signature:

function middleware(req, res, next) { โ€ฆ }

Error-handling middleware has four parameters (Express detects the arity):

function errorHandler(err, req, res, next) { โ€ฆ }

Middleware runs in order โ€” every app.use or route-specific middleware is pushed onto a stack.

๐Ÿ’ป Code Example โ€” Basic Middleware

import express from "express";
const app = express();

// Built-in
app.use(express.json());                     // parses JSON bodies
app.use(express.urlencoded({ extended: true }));

// Custom โ€” logger
app.use((req, res, next) => {
  console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
  next();                                     // pass control
});

app.get("/", (req, res) => res.send("Hello"));

app.listen(3000);

๐Ÿ’ป Code Example โ€” Auth Middleware

import jwt from "jsonwebtoken";

export function authMiddleware(req, res, next) {
  const header = req.headers.authorization;
  if (!header?.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Missing token" });
  }

  try {
    const payload = jwt.verify(header.slice(7), process.env.JWT_SECRET);
    req.user = payload;                       // attach for downstream handlers
    next();
  } catch (err) {
    res.status(401).json({ error: "Invalid token" });
  }
}

// Apply only to /api
app.use("/api", authMiddleware);

๐Ÿ’ป Code Example โ€” Role-Based Authorization

export function requireRole(role) {
  return (req, res, next) => {
    if (!req.user) return res.status(401).json({ error: "Not logged in" });
    if (req.user.role !== role) return res.status(403).json({ error: "Forbidden" });
    next();
  };
}

app.delete("/api/users/:id", authMiddleware, requireRole("admin"), deleteUser);

๐Ÿ’ป Code Example โ€” Validation Middleware

import { body, validationResult } from "express-validator";

const validateUser = [
  body("email").isEmail(),
  body("password").isLength({ min: 8 }),
  (req, res, next) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    next();
  },
];

app.post("/register", validateUser, registerHandler);

๐Ÿ’ป Code Example โ€” Async Middleware (Error Forwarding)

// Wrapper that catches rejected promises and forwards to error handler
export const asyncHandler = (fn) => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

// Usage
app.get("/users/:id", asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) throw new AppError(404, "User not found");
  res.json(user);
}));

๐Ÿ’ป Code Example โ€” Centralized Error Handler

class AppError extends Error {
  constructor(status, message) { super(message); this.status = status; }
}

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: {
      message: err.message || "Internal Server Error",
      ...(process.env.NODE_ENV !== "production" && { stack: err.stack }),
    },
  });
});

The error handler MUST be defined after all routes and with 4 parameters.

๐Ÿ’ป Code Example โ€” Conditional Middleware

function corsForApi(req, res, next) {
  if (req.path.startsWith("/api")) {
    res.set("Access-Control-Allow-Origin", "*");
  }
  next();
}

app.use(corsForApi);

๐Ÿ’ป Code Example โ€” Third-Party Middleware Stack

import express from "express";
import helmet from "helmet";
import cors from "cors";
import morgan from "morgan";
import rateLimit from "express-rate-limit";
import compression from "compression";

const app = express();

app.use(helmet());                            // secure headers
app.use(cors({ origin: ["https://app.example.com"] }));
app.use(express.json({ limit: "1mb" }));
app.use(morgan("combined"));                  // access logs
app.use(compression());                       // gzip responses
app.use("/api/auth", rateLimit({ windowMs: 15*60*1000, max: 5 }));

app.use("/api", apiRoutes);
app.use(errorHandler);

๐ŸŒ Real-World Impact

  • Middleware separates cross-cutting concerns from business logic.
  • Order matters โ€” helmet and cors should come first; error handlers last.
  • In NestJS, the same pattern is reinforced via guards, interceptors, and pipes (see Chapter 41).

๐ŸŽฏ Likely Interview Questions

  1. What is middleware?
  2. What is the signature of error-handling middleware in Express?
  3. How do you handle async errors in Express?
  4. In what order does middleware run?
  5. Difference between middleware in Express vs guards in NestJS?

โ† async/await | Index | Next: JWT Authentication โ†’