A middleware function is a plain function of (req, res, next). It must do exactly one of three things: call next() to continue, call next(err) to divert to error handling, or end the response itself. Doing none hangs the request until the client times out; doing two throws ERR_HTTP_HEADERS_SENT on the second write.
The timer below measures how long a request took. It cannot do that by timing next(), which returns as soon as the next layer yields rather than when the response is finished. Instead it records a start time and listens for the response stream's finish event.
export function requestTimer(req, res, next) {
const start = process.hrtime.bigint();
res.on('finish', () => {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} ${ms.toFixed(1)}ms`);
});
next();
}GET /health 200 4.6ms GET /books 401 0.8ms GET /books 200 0.4ms
Three habits separate middleware that survives production from middleware that does not. Use process.hrtime.bigint rather than Date.now for durations: the monotonic clock is immune to system clock changes. Name the function instead of passing an arrow expression, so it appears in stack traces and in the router's layer list. And attach data to a namespaced property or a symbol, so two packages cannot collide over req.user.