Node.js cookbook · Beginner

Centralize error handling in Express 5

Return consistent error responses and never leak stack traces, including for async handler failures.

Approach

Throw typed HttpErrors from handlers and services, let Express 5 route async rejections to a single error-handling middleware (4 arguments), log unexpected errors with request context, and return Problem Details JSON. Add a 404 handler before it.

Solution

import express, { type ErrorRequestHandler } from "express";

export class HttpError extends Error {
  constructor(public status: number, public title: string, public detail?: string) { super(detail ?? title); }
}
export const notFound = (what: string) => new HttpError(404, "Not Found", `${what} not found`);

const app = express();
app.use(express.json({ limit: "100kb" }));

app.get("/api/orders/:id", async (req, res) => {
  const order = await orders.find(req.params.id);
  if (!order) throw notFound("Order");            // Express 5 catches async throws
  res.json(order);
});

app.use((req, res) => {
  res.status(404).type("application/problem+json").json({ title: "Not Found", status: 404 });
});

const errorHandler: ErrorRequestHandler = (err, req, res, _next) => {
  if (err instanceof HttpError) {
    return res.status(err.status).type("application/problem+json")
      .json({ title: err.title, status: err.status, detail: err.detail });
  }
  if (err?.type === "entity.parse.failed") {
    return res.status(400).type("application/problem+json").json({ title: "Malformed JSON", status: 400 });
  }
  req.log?.error({ err }, "unhandled error");     // pino-http logger
  res.status(500).type("application/problem+json").json({ title: "Internal Server Error", status: 500 });
};
app.use(errorHandler);

Pitfalls

  • Express 4: async errors aren't caught without wrappers (upgrade to Express 5 or wrap handlers).
  • Sending err.message for unexpected errors.
  • Error middleware with only 3 parameters (Express won't treat it as an error handler).

Variations

  • Fastify's setErrorHandler with schema-based validation errors.

Further reading: Express: Error handling

Go deeper: Express: The Middleware Stack and Error Handling in the Request Lifecycle course traces this step of the request, with a production bug and a runnable simulation.

Reference: https://expressjs.com/en/guide/error-handling.html

More Node.js recipes