Script Valley
Node.js: The Complete Runtime
Express.js: Building REST APIsLesson 4.4

Structured error handling in Express APIs

custom AppError class, operational vs programmer errors, async error wrapper, error middleware, HTTP status codes, error response format, 404 handler

A Custom Error Class Carries HTTP Status

Create one error class for all known API errors so your error middleware can format them consistently.

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

Async Error Wrapper

Express 4 does not catch errors thrown inside async route handlers by default. Wrap them:

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 AppError('User not found', 404);
  res.json(user);
}));

Express 5 handles async errors natively — no wrapper needed.

Global Error Handler

app.use('*', (req, res) => res.status(404).json({ error: 'Route not found' }));

app.use((err, req, res, next) => {
  if (err.isOperational) {
    return res.status(err.statusCode).json({ error: err.message });
  }
  console.error('UNHANDLED:', err);
  res.status(500).json({ error: 'Something went wrong' });
});

Up next

Serving files and handling multipart uploads in Express

Sign in to track progress