BugForge
All lessons
Node.js · 02/19 7 min

Express request pipeline

Middleware order, next(), error handlers with four arguments, and 'headers already sent'.

express.json()
→
cors()
→
requireAuth
→
router
→
handler
→
404 fallback
→
errorHandler
200Every middleware calls next(); the handler responds with res.json(). Nothing after it runs.

An Express app is a list of functions tried in registration order. Each one either responds or calls next() to pass control on.

app.use(express.json());          // 1. parse body → req.body
app.use(cors());                  // 2. headers
app.use("/api/orders", requireAuth, ordersRouter); // 3. auth only for this mount
app.use((req, res) => res.status(404).json({ error: "Not found" })); // 4. fallthrough
app.use((err, req, res, next) => { /* 5. error handler: FOUR args */ });

Rules that cause real bugs

  • Order matters. Middleware registered after a route never runs for that route. express.json() must come before handlers that read req.body.
  • An error handler is recognised by arity. (err, req, res, next) with four parameters. Drop next and Express treats it as normal middleware.
  • next(err) skips to the error handler; next() continues normally; next('route') skips the rest of the current route's handlers.
  • Respond once. res.json() followed by another res.send() throws Cannot set headers after they are sent. Usually a missing return before an early response:
if (!user) res.status(404).json({ error: "no user" }); // ❌ falls through
if (!user) return res.status(404).json({ error: "no user" }); // ✅
  • Async errors (Express 4): wrap handlers so rejections reach next:
const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
  • req.params and req.query are strings. req.query.limit + 1 is "101".
Practise it now
Next in Node.js
Async pitfalls catalogue