Global Error Handling

A professional API doesn't just crash or return a generic "500 Internal Server Error". It returns meaningful, typed, and consistent error objects.

1. The Consistent Error Body

Use a predictable structure so clients can easily handle errors in their frontend code.

{
  "status": "error",
  "error": {
    "type": "VALIDATION_ERROR",
    "message": "Password is too short",
    "details": { "field": "password", "min": 8 }
  }
}

2. Operational vs Programmer Errors

  • Operational: Errors we can predict (e.g., 404 Found, 400 Validation Failed). Return clean messages.
  • Programmer: Bugs or unexpected crashes. Log internally, return a generic "Try again later" to the client.

3. Centralized Middleware

Don't use try/catch in every route. Use a global error handler.

app.use((err, req, res, next) => {
  const status = err.statusCode || 500;
  res.status(status).json({
    status: 'error',
    message: err.message
  });
});

4. Environment-Aware Errors

In Development, send the full stack trace to the client. In Production, hide it for security.

Best Practice: Log every 500-level error to a service like Sentry or Datadog immediately. Silent failures are the hardest to debug in distributed systems.