Node.js API Best Practices

Chapter 2 · Practice 2 of 4

Handle errors in one central error handler

From Validation and error handling

Why it matters

Scattering try/catch blocks with their own response code in every route leads to inconsistent status codes and leaked messages. Handle errors in one place instead.

  1. Throw typed errors from services, such as NotFoundError or ConflictError.
  2. Turn them into HTTP responses in one error handler.
  3. Map known errors to 4xx codes.
  4. Log unknown errors with full details and answer them with a generic 500.

Express 5 forwards rejected promises and errors thrown in async handlers to the error middleware automatically, so you no longer need wrappers or .catch(next). In Fastify, use setErrorHandler.

Example

TypeScript
class HttpError extends Error {
  constructor(public status: number, message: string) {
    super(message);
  }
}

app.use((err, req, res, next) => {
  if (err instanceof HttpError) return res.status(err.status).json({ error: err.message });
  req.log.error({ err }, 'unhandled error');
  res.status(500).json({ error: 'Something went wrong' });
});