Good Code
The good version centralizes error mapping, checks headersSent, and keeps unexpected error output generic.
Lesson 06
Use one Express error handler to map application errors into HTTP responses.
import type { ErrorRequestHandler } from "express";
export const errorHandler: ErrorRequestHandler = (error, _req, res, next) => {
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." });
};router.post("/", async (req, res) => {
try {
const review = await service.createReview(req.body);
res.json(review);
} catch (error) {
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" });
}
});The good version centralizes error mapping, checks headersSent, and keeps unexpected error output generic.
The bad version repeats catch blocks in routes and returns different shapes and status codes for similar failures.