Express Error Handling System

mediumTypeScript

Lesson

Error Handling in Express Applications

Error handling is crucial for building reliable Express applications. Without proper error handling, unhandled promise rejections and thrown errors can crash your Node.js server, leading to poor user experience and system instability.

Express provides a built-in error handling mechanism, but it requires following specific patterns. Express error handlers are special middleware functions that accept four parameters: (err, req, res, next). When an error occurs in your application, Express will skip all regular middleware and route handlers, jumping directly to error handling middleware.

The challenge with async route handlers is that Express doesn't automatically catch promise rejections. If an async function throws an error or returns a rejected promise, Express won't catch it unless you explicitly handle it. This is where async wrappers become essential.

Custom error classes extend the built-in Error object to provide additional context. By creating an AppError class with properties like statusCode and isOperational, you can distinguish between expected errors (like "user not found") and unexpected programming bugs. This distinction is valuable for logging, monitoring, and deciding whether to expose error details to clients.

The three-layer approach—custom error classes, async wrappers, and global error handlers—creates a robust system. Custom errors provide structure, async wrappers ensure all errors are caught, and global handlers provide consistent error responses across your entire application.

Example
1// Custom error class 2class ValidationError extends Error { 3 constructor(message, field) { 4 super(message); 5 this.field = field; 6 this.statusCode = 400; 7 } 8} 9 10// Async wrapper function 11const catchAsync = (fn) => { 12 return (req, res, next) => { 13 Promise.resolve(fn(req, res, next)).catch(next); 14 }; 15}; 16 17// Usage in routes 18app.get('/profile', catchAsync(async (req, res) => { 19 const user = await User.findById(req.params.id); 20 if (!user.email) { 21 throw new ValidationError('Email is required', 'email'); 22 } 23 res.json(user); 24}));
L3Adding custom properties to errors provides more context
L10Promise.resolve() handles both sync and async functions safely
L16Thrown errors are automatically caught and passed to error handlers

Key Takeaways

  • •Express error handlers must have exactly 4 parameters to be recognized by Express
  • •Async route handlers need wrappers to ensure promise rejections are caught
  • •Custom error classes help distinguish between operational errors and programming bugs
Loading...