Script Valley
Express.js: APIs and Middleware
Building RESTful APIsLesson 3.3

Express error handling middleware — how to catch all errors

error handler middleware, 4-parameter signature, next(err), app.use placement, operational vs programmer errors, custom AppError class, centralized error handling

Centralized Error Handling in Express

Express has a built-in error-handling mechanism: any middleware with four parameters (err, req, res, next) is treated as an error handler. You pass errors to it by calling next(err).

Custom error class

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

module.exports = AppError;

Global error handler

// middleware/errorHandler.js
function errorHandler(err, req, res, next) {
  const statusCode = err.statusCode || 500;
  const message = err.isOperational ? err.message : 'Internal server error';

  console.error(`[ERROR] ${err.message}`, err.stack);

  res.status(statusCode).json({
    success: false,
    error: { message, code: err.code || 'INTERNAL_ERROR' }
  });
}

module.exports = errorHandler;

Using it

// In routes — throw operational errors
app.get('/users/:id', (req, res, next) => {
  const user = findUser(req.params.id);
  if (!user) return next(new AppError('User not found', 404, 'USER_NOT_FOUND'));
  res.json(user);
});

// In app.js — register AFTER all routes
app.use(errorHandler);

Register the error handler after all routes. The error handler is the last safety net — uncaught programmer errors should log a stack trace but show a generic message to clients.

Up next

Pagination, filtering, and sorting in REST APIs

Sign in to track progress