How Middleware Works — The Chain Pattern
Express middleware is a function with signature (req, res, next). Every request flows through the middleware chain in the order they are registered. Understanding this chain is the key to debugging why a request is being transformed, rejected, or logged unexpectedly.
Middleware Chain Internals
import express, { Request, Response, NextFunction } from 'express';
// Middleware type: (req, res, next) => void
// Call next() to continue the chain
// Call next(err) to jump to ERROR middleware
// Call res.send/json/end to terminate the chain
const app = express();
// ━━ Regular middleware (called for every request) ━━
app.use((req: Request, res: Response, next: NextFunction) => {
console.log(`[${req.method}] ${req.path}`); // logs every request
next(); // MUST call next() or the request hangs forever
});
// ━━ Route-specific middleware ━━
function requireAuth(req: Request, res: Response, next: NextFunction): void {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
res.status(401).json({ error: 'No token' });
return; // ← return after sending, or next() will be called twice
}
// Attach user to req for downstream handlers
(req as any).user = verifyToken(token);
next();
}
// ━━ Multiple middleware on a single route ━━
app.get('/admin',
requireAuth, // middleware 1: verify token
requireRole('admin'), // middleware 2: check role
async (req, res) => { // handler: actual business logic
res.json({ message: 'Admin area' });
}
);
// ━━ Router-level middleware ━━
import { Router } from 'express';
const protectedRouter = Router();
protectedRouter.use(requireAuth); // applies to ALL routes on this router
protectedRouter.get('/profile', (req, res) => res.json({ profile: {} }));
protectedRouter.get('/settings', (req, res) => res.json({ settings: {} }));
app.use('/api/v1', protectedRouter);
function verifyToken(token: string) { return { id: '1', role: 'user' }; }
function requireRole(role: string) {
return (req: Request, res: Response, next: NextFunction) => { next(); };
}Tip
Tip
Practice How Middleware Works The Chain Pattern in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Each middleware calls next() to pass control or sends a response
Practice Task
Note
Practice Task — (1) Write a working example of How Middleware Works The Chain Pattern from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with How Middleware Works The Chain Pattern is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready node code.