ENGINEERING NOTES
Express.js Request Lifecycle: Middleware to Error Handling
Trace a request through ordered middleware, validation, routing and a safe centralized error boundary.
What problem does this solve?
Order-dependent middleware can leave routes unprotected, requests hanging or responses written twice. An explicit pipeline makes authentication, validation and error behavior reviewable.
What is it?
Express registers middleware and route handlers in order. A handler can end the response or pass control with next. Error middleware uses four parameters and belongs after the routes whose failures it should handle.
Mental model
A request passes checkpoints. Each checkpoint either rejects it, answers it or hands it onward. An error takes a separate exit route. Calling next after sending a final response can accidentally send it through another checkpoint.
How it works internally
Express matches method, path and middleware registration order. next(error) skips ordinary handlers toward an error handler. Express 5 forwards rejected returned promises; Express 4 requires an explicit async error bridge. Errors from detached timers or unreturned async work still need explicit handling.
Production example
Put bounded body parsing and request context early, authentication before protected routes, schema validation at the HTTP boundary, and centralized error translation last. A 404 handler is needed separately because no matching route is not itself an exception.
Node.js / TypeScript implementation
Minimal Express 5 TypeScript application; requires express and its types. Validation and authentication for a real write endpoint belong before its controller. The generic 500 response avoids leaking internal errors.
import express from "express";
import type { ErrorRequestHandler } from "express";
const app = express();
app.use(express.json({ limit: "32kb" }));
app.get("/live", (_req, res) => { res.json({ ok: true }); });
app.use((_req, res) => { res.status(404).json({ error: "Not found" }); });
const errors: ErrorRequestHandler = (error, _req, res, next) => {
if (res.headersSent) { next(error); return; }
res.status(500).json({ error: "Request failed" });
};
app.use(errors);
app.listen(3000);Common mistakes
Do not put a protected route before its authentication middleware. Do not assume TypeScript types validate incoming JSON. Do not expose raw SQL/provider errors or treat every exception as a successful HTTP 200.
Performance / reliability considerations
Bound payloads and upstream deadlines. Separate liveness from dependency readiness. Classify expected validation and authentication failures before generic server failures. Correlate errors using request IDs while redacting credentials and session values.
Interview questions
Why does middleware order matter? What changes for async handlers between Express 4 and 5? Why is a 404 handler separate? What should happen if headers are already sent?
Quick Revision
Order matters. A middleware must end the response or pass control. next(error) uses the error path. Return/await asynchronous work. Express 5 forwards returned promise failures. Validate input at runtime. Put 404 and error handling last; expose safe messages and keep diagnostic context private.