app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next(); // hand off to whatever's next — no await needed
});
app.use(authenticate);
app.post("/returns/:returnId/approve", approveReturn);
Same handoff, no await needed — calling next() is enough to move the request along. Skip that call, or skip sending a response some other way, and an Express request doesn't fail loudly. There's no exception, no automatic timeout kicking in. The connection just stays open, waiting on a reply that nothing left in the chain is ever going to send, until whatever's on the other end eventually gives up on its own.
// Runs for every route below this line
app.use(express.json());
// Runs only for this one route, layered in as extra arguments
app.post("/returns/:returnId/approve", authenticate, requireManager, approveReturn);
// Runs for every route under this one prefix
app.use("/admin", requireManager, adminDashboardRouter);
Express doesn't give branching a dedicated keyword the way ASP.NET Core's Map does — a path prefix handed to app.use and a stack of functions placed ahead of one route's handler are really the same registration-order trick, just aimed at two different scopes: one covers an entire URL prefix, the other covers a single route. Counter's regional dashboard picks up its manager check exactly the same way the approval route does — one more entry in the list, this one scoped to everything under /admin.
app.post("/returns/:returnId/approve", authenticate, requireManager, (req, res, next) => {
approveReturn(req.params.returnId, req.body)
.then((result) => res.json(result))
.catch(next); // hands the rejection to Express's error-handling chain
});
// FOUR parameters — err, req, res, next — is the entire signal Express needs
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status || 500).json({ error: "could not process the return" });
});
Express decides whether a middleware is an error handler purely by counting its parameters. Four instead of three flags it as special-cased, and it only fires once something further up the chain hands next a value — either through an explicit next(err), or through a plain throw inside a synchronous handler. The moment that happens, Express stops offering the request to any ordinary three-argument function still queued up and scans forward until it lands on one built to take four. That scan is the entire error-routing mechanism — there's nothing more elaborate underneath it.
That .catch(next) on approveReturn(...) is doing real work, not decoration — and exactly how much work depends on which major version of Express is actually running underneath it, a wrinkle worth flagging now and coming back to properly once the error-handling chapter gets there.