Topic 3 of 55
Middleware Pattern
Overview
Middleware functions are the backbone of Express — they have access to request, response, and the next middleware function. They enable cross-cutting concerns like authentication, logging, CORS, compression, and error handling without polluting route logic.
Syntax
javascript
// Middleware signature
function myMiddleware(req, res, next) {
// do something
next(); // pass to next middleware/route
// OR
res.json(); // end the request
}
// Error handling middleware (4 params — Express detects by arity)
function errorHandler(err, req, res, next) {
console.error(err.stack);
res.status(err.status || 500).json({ error: err.message });
}
// Applying middleware
app.use(globalMiddleware); // applies to ALL routes
app.use('/api', apiOnlyMiddleware); // applies to /api/* routes
app.get('/protected', authenticate, routeHandler); // route-specificCommon Pitfalls
- Error middleware MUST have exactly 4 parameters (err, req, res, next) — Express won't treat 3-parameter functions as error handlers.
- Middleware runs in registration order — register general middleware before routes, error middleware last.
- Interview tip: Middleware is the implementation of the Chain of Responsibility design pattern in Express.
Real-World Example
Authentication and rate limiting middleware:
example
javascript
import jwt from 'jsonwebtoken';
// Authentication middleware
export function authenticate(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'No token provided' });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // attach user to request
next();
} catch {
res.status(401).json({ error: 'Invalid or expired token' });
}
}
// Role authorization middleware factory
export function authorize(...roles) {
return (req, res, next) => {
if (!roles.includes(req.user?.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Request logging middleware
export function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} ${res.statusCode} ${duration}ms`);
});
next();
}
// Usage
app.get('/admin/users', authenticate, authorize('admin', 'superadmin'), getUsers);