All posts

Express Error Handling Patterns That Scale

Master Express.js error handling with patterns for async errors, custom error classes, centralized handlers, and operational error recovery.

Express Error Handling Patterns That Scale

Default Express error handling sends a bare HTML page to clients. Here's how to build something production-worthy.

Custom Error Classes

Create a hierarchy of error types:

class AppError extends Error {
  constructor(message, statusCode, isOperational = true) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = isOperational;
    Error.captureStackTrace(this, this.constructor);
  }
}

class NotFoundError extends AppError {
  constructor(resource = 'Resource') {
    super(`${resource} not found`, 404);
  }
}

class ValidationError extends AppError {
  constructor(errors) {
    super('Validation failed', 400);
    this.errors = errors;
  }
}

Async Error Wrapper

Express doesn't catch async errors by default. Wrap your route handlers:

const asyncHandler = (fn) => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

app.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) throw new NotFoundError('User');
  res.json(user);
}));

Centralized Error Handler

app.use((err, req, res, next) => {
  // Log all errors
  logger.error({
    message: err.message,
    stack: err.stack,
    path: req.originalUrl,
    method: req.method,
  });

  if (err.isOperational) {
    return res.status(err.statusCode).json({
      status: 'error',
      message: err.message,
      ...(err.errors && { errors: err.errors }),
    });
  }

  // Programming errors — don't leak details
  res.status(500).json({
    status: 'error',
    message: 'Something went wrong',
  });
});

Operational vs. Programming Errors

  • Operational errors (user not found, invalid input): expected, respond with appropriate status codes
  • Programming errors (undefined property access, type errors): unexpected, log the full stack trace and return generic 500

This distinction is critical. Operational errors are normal; programming errors need immediate attention. Bugsly helps by flagging new error types so you can quickly distinguish between the two and prioritize fixes accordingly.

Try Bugsly Free

AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.

Get Started Free