Concept
The Middleware Signature
In Express, middleware functions have access to the Request object (req), the Response object (res), and the next function in the application's request-response cycle.
function myMiddleware(req, res, next) {
// Perform operations...
next(); // Pass control to the next handler
}A middleware function can:
- Execute any code.
- Make changes to the request and response objects.
- End the request-response cycle (e.g. sending a 403 Forbidden).
- Call the
nextmiddleware in the stack.
The Onion Execution Model
Express middleware operates on an onion model (similar to Redux middleware). When next() is called, control yields forward. Once the route handler executes, the call stack unwinds backward, executing post-next statements:
app.use(loggerMiddleware);app.use('/admin', authMiddleware);app.get('/admin/dashboard', (req, res) => res.send('Dashboard'));
Client sends a request. Express routes the request into the global middleware stack, hitting the first-registered logger middleware.
Middleware Scopes
- Global Scope: Applies to all routes.
app.use(express.json()); // parses body for all routes - Router Scope: Applies to a mounted router prefix.
adminRouter.use(authMiddleware); - Route Scope: Applies to a single route.
app.get('/dashboard', authMiddleware, (
Common Mistakes
1. Forgetting to call next()
If a middleware function does not call next() and does not send a response (like res.send()), the connection sits open, causing client requests to hang indefinitely until a timeout occurs.
2. Calling next() after completing a response
If you execute res.send('Done') and subsequently call next(), Express continues routing, which can execute downstream handlers and trigger headers-already-sent crashes.
Best Practices
- Mutate request context safely: Attach properties to
req(likereq.user = decodedToken) so that subsequent handlers can access authentication contexts easily. - Isolate middleware concerns: Keep functions small and focused on a single task (e.g., one for parsing cookies, one for authentication, one for validation).
- Handle errors in middleware: Catch async errors and pass them to
next(error)to route them to the central error handling middleware.
