Express

Lesson 06

Central error handler

Use one Express error handler to map application errors into HTTP responses.

Good Code

src/errors.ts
import type { ErrorRequestHandler } from "express";

export const errorHandler: ErrorRequestHandler = (error, _req, res, next) => {
  // Never write a second response after headers were sent.
  if (res.headersSent) {
    next(error);
    return;
  }

  if (error.name === "ValidationError") {
    res.status(400).json({ error: error.message });
    return;
  }

  console.error({ error }, "Unhandled request error");
  res.status(500).json({ error: "Internal server error." });
};

Bad Code

src/reviews/router.ts
router.post("/", async (req, res) => {
  try {
    const review = await service.createReview(req.body);
    res.json(review);
  } catch (error) {
    // Each route invents its own error shape and status code.
    console.error(error);
    res.status(500).send("Something broke");
  }
});

router.get("/:id", async (req, res) => {
  try {
    res.json(await service.getReview(req.params.id));
  } catch {
    res.status(400).json({ message: "Bad request" });
  }
});

Review Notes

What to review

Good Code

The good version centralizes error mapping, checks headersSent, and keeps unexpected error output generic.

Bad Code

The bad version repeats catch blocks in routes and returns different shapes and status codes for similar failures.

Takeaways

  • A central error handler keeps status codes, logging, and response bodies consistent.