Express Middleware
Express Middleware
Section titled “Express Middleware”Middleware functions are the backbone of Express. They have access to the request, response, and next function, and can modify the request/response or end the request cycle.
Middleware Anatomy
Section titled “Middleware Anatomy”import { Request, Response, NextFunction } from 'express';
function myMiddleware(req: Request, res: Response, next: NextFunction) { // Do something before the route handler console.log(`${req.method} ${req.path}`);
// Call next() to pass control to the next middleware or route next();
// If you call next(error), Express skips to error-handling middleware // next(new Error('Something went wrong'));}
app.use(myMiddleware); // applies to all routesapp.use('/api', myMiddleware); // applies to /api/* routes onlyapp.get('/path', myMiddleware, handler); // applies to this route onlyExecution Order
Section titled “Execution Order”Middleware runs in registration order:
app.use(cors()); // 1. CORS headersapp.use(helmet()); // 2. Security headersapp.use(express.json()); // 3. Parse request bodyapp.use(requestLogger); // 4. Log incoming requestapp.use('/api', router); // 5. Routesapp.use(errorHandler); // 6. Error handler (must be last)Request Logging
Section titled “Request Logging”function requestLogger(req: Request, res: Response, next: NextFunction) { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`); }); next();}Or use morgan:
npm install morgannpm install -D @types/morganimport morgan from 'morgan';app.use(morgan('dev')); // concise output for developmentapp.use(morgan('combined')); // Apache-style for productionnpm install corsnpm install -D @types/corsimport cors from 'cors';
// Allow all origins (development)app.use(cors());
// Restrict to specific originsapp.use(cors({ origin: ['https://myapp.com', 'https://staging.myapp.com'], methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'],}));Security Headers with Helmet
Section titled “Security Headers with Helmet”npm install helmetimport helmet from 'helmet';app.use(helmet()); // sets 15+ security-related HTTP headersRate Limiting
Section titled “Rate Limiting”npm install express-rate-limitimport rateLimit from 'express-rate-limit';
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per window standardHeaders: true, legacyHeaders: false, message: { error: 'Too many requests, please try again later' },});
app.use('/api', limiter);
// Stricter limit for auth endpointsconst authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 10 });app.use('/api/auth', authLimiter);Authentication Middleware
Section titled “Authentication Middleware”import jwt from 'jsonwebtoken';
export function requireAuth(req: Request, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ error: 'Authentication required' }); }
const token = authHeader.slice(7); try { const payload = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload; req.user = { id: payload.sub as string }; next(); } catch (err) { return res.status(401).json({ error: 'Invalid or expired token' }); }}
// Attach to protected routesrouter.get('/profile', requireAuth, getProfile);router.post('/posts', requireAuth, createPost);Error Handling Middleware
Section titled “Error Handling Middleware”Error middleware must have exactly four parameters. Express identifies it by the err parameter:
function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) { console.error(err.stack);
if (err.name === 'ValidationError') { return res.status(400).json({ error: err.message }); }
if (err.name === 'UnauthorizedError') { return res.status(401).json({ error: 'Unauthorized' }); }
res.status(500).json({ error: process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message, });}
// Must be registered AFTER all other middleware and routesapp.use(errorHandler);Async Error Handling
Section titled “Async Error Handling”Express doesn’t catch async errors automatically. Wrap async route handlers:
// Manual approachapp.get('/users', async (req, res, next) => { try { const users = await userService.findAll(); res.json(users); } catch (err) { next(err); // passes error to error handler }});
// Helper to avoid boilerplateconst asyncHandler = (fn: RequestHandler): RequestHandler => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
app.get('/users', asyncHandler(async (req, res) => { const users = await userService.findAll(); res.json(users);}));Extending the Request Type
Section titled “Extending the Request Type”declare global { namespace Express { interface Request { user?: { id: string; role: string }; userId?: string; } }}