Node.js › Error Handling

What is a centralized error-handling middleware pattern in Express, and what advantages does it provide over handling errors in each individual route?

Advanced

Answer

Express supports a special error-handling middleware signature (with four parameters: err, req, res, next) placed at the end of the middleware chain -- any error passed to next(err) anywhere in the request pipeline (or thrown inside an async route wrapped appropriately) is routed to this single centralized handler, letting error formatting, logging, and status-code mapping happen consistently in one place rather than duplicated across every route handler.
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await getUserOrThrow(req.params.id);
    res.json(user);
  } catch (err) {
    next(err); // delegate to centralized handler
  }
});

app.use((err, req, res, next) => { // must have exactly 4 params
  logger.error(err);
  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({ error: err.isOperational ? err.message : 'Internal server error' });
});
Real-world example An API refactors dozens of route handlers, each with their own duplicated try/catch error-formatting logic, to instead call next(err) and rely on a single centralized error-handling middleware, immediately making error responses consistent across the entire API and simplifying every individual route handler.

Common follow-ups: Why does Express require an error-handling middleware to have exactly four parameters to recognize it as such?;How do you ensure an unhandled error inside an async route handler actually reaches this middleware, given Express doesn't automatically catch async errors by default in older versions?