CodeOath
← All posts
Architecture & Patterns55 min total · 13 parts

Middleware Pipelines Compared: ASP.NET Core, Express.js, and Django

Contents — Part 4 of 13: Express.js
Part 4 of 13 · ~2 min

Express.js

app.use((req, res, next) => {
  console.log(`Incoming: ${req.path}`);
  next(); // pass control down the chain
});

app.use(authenticate);
app.use(authorize);
app.get("/orders/:id", getOrderHandler);

Same shape, different keyword: calling next() (instead of awaiting it) hands off to the next middleware. Forgetting to call next() — or to send a response — is the classic Express bug: the request just hangs forever, satisfying neither path, with no error and no timeout by default.

Middleware order and route-specific middleware

// Applied to EVERY route, because it's registered before the routes with app.use
app.use(express.json());

// Applied to only ONE route, as an extra argument before the handler
app.get("/orders/:id", authenticate, getOrderHandler);

// Applied to every route under a specific path prefix
app.use("/admin", adminAuthMiddleware);

Express doesn't have a separate "branch" concept the way ASP.NET Core's Map does — instead, app.use(path, middleware) scopes a middleware to a path prefix, and passing extra functions before the final handler in a route definition scopes middleware to just that one route. Both are just variations on the same registration order the whole pattern depends on.

Error-handling middleware has a distinct signature

app.get("/orders/:id", (req, res, next) => {
  fetchOrder(req.params.id)
    .then(order => res.json(order))
    .catch(next);  // forwards the error to Express's error-handling chain
});

// An error handler is identified by having FOUR parameters, not three
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: "Something went wrong" });
});

Express distinguishes an error-handling middleware purely by arity — a function with four parameters (err, req, res, next) instead of three is treated specially and only invoked when something calls next(err) (or a synchronous handler throws). Passing anything to next() other than nothing skips every remaining normal middleware and jumps straight to the nearest error handler — this is the mechanism by which Express implements short-circuiting for the error path specifically.