Middleware
Express middleware are functions that receive (req, res, next). They run in order; each can short-circuit by responding or pass control with next(). Auth, logging, validation, error handling — all middleware.
Built-in + custom + error handling
EXAMPLE
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import rateLimit from 'express-rate-limit';
const app = express();
// 1) Global middleware — order matters!
app.use(helmet()); // security headers
app.use(cors({ origin: 'https://app.example.com', credentials: true }));
app.use(morgan('combined')); // request logging
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ extended: true }));
// 2) Rate limit
app.use('/api', rateLimit({
windowMs: 60_000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
}));
// 3) Custom middleware — adding a request ID
app.use((req, _res, next) => {
req.id = crypto.randomUUID();
next();
});
// 4) Auth middleware — short-circuit on failure
function requireAuth(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'no token' });
try {
req.user = verifyJWT(token);
next();
} catch (e) {
return res.status(401).json({ error: 'invalid token' });
}
}
// 5) Apply to specific routes
app.get('/me', requireAuth, getProfile);
app.use('/admin', requireAuth, requireRole('admin'), adminRouter);
// 6) Validation middleware
import { z } from 'zod';
const validate = (schema) => (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) return res.status(400).json({ errors: result.error.issues });
req.body = result.data;
next();
};
app.post('/users', validate(NewUser), createUser);
// 7) Async middleware — wrap to forward errors
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await db.users.findOne({ id: req.params.id });
if (!user) throw new HttpError(404, 'not found');
res.json(user);
}));
// 8) Error-handling middleware — 4 args, must be LAST
app.use((err, req, res, _next) => {
log.error({ reqId: req.id, err }, 'unhandled');
res.status(err.status ?? 500).json({ error: err.message ?? 'internal' });
});
// 9) Conditional middleware — apply only when needed
const inDev = process.env.NODE_ENV !== 'production';
if (inDev) app.use(require('morgan')('dev'));
// 10) Router-level middleware
import { Router } from 'express';
const posts = Router();
posts.use(requireAuth); // all posts routes need auth
posts.get('/', listPosts);
posts.post('/', validate(NewPost), createPost);
app.use('/posts', posts);
Why it matters
Middleware order IS the request pipeline. Keep security headers + CORS + body parsing at the top; auth before the route; error handler at the bottom. The mental model is “outermost to innermost.”
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
function logger(req, res, next) {
console.log(req.method, req.url);
next();
}
app.use(logger);
Try it Yourself »
Discussion
Loading…