Course contentsShow
← Node.js
Lesson 730 of 1,9807. Web Framework Architecture and MiddlewareFree lesson

Modular Error Handling

Create hierarchical error handling that specializes behavior based on exception type and context.

In this lesson, you will learn how to design a hierarchical error-handling system that separates general system failures from specific business logic violations.

The Problem with Monolithic Try-Catch

Beginners often clutter their route handlers with generic try/catch blocks that return a 500 status code for every problem. This is problematic because a database connection timeout (a system error) requires a different response strategy than a user entering an invalid email format (a business logic error).

Creating a Hierarchy

Modular error handling treats errors as objects with specific properties. By creating a base class and extending it, we can create specialized "error types."

// Base class for all operational errors
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
  }
}

// Specialized Business Logic Error
class ValidationError extends AppError {
  constructor(message) {
    super(message, 400); // 400 Bad Request
  }
}

Contextualizing with Middleware

Because we are using a Pipeline Extensibility Architecture, we can place a centralized error-handling middleware at the very end of our middleware chain. This middleware inspects the instance type of the error to decide how to respond:

app.use((err, req, res, next) => {
  if (err instanceof ValidationError) {
    return res.status(err.statusCode).json({ type: 'Validation', message: err.message });
  }
  
  // Default fallback for unexpected system failures
  res.status(500).json({ type: 'System', message: 'Internal Server Error' });
});

Why This Matters

By distinguishing between types, your application becomes predictable. Your Business Logic Policy Enforcement layer can throw a ValidationError knowing exactly how the system will react. Meanwhile, infrastructure-level failures (like a disconnected cache) can be handled by a different branch of your logic, ensuring that your API doesn't leak sensitive stack traces or internal implementation details to the client. This modular approach keeps your route handlers clean and ensures that your error strategy scales alongside your application.

Key Takeaway: By categorizing errors into a class hierarchy, you can automate sophisticated, context-aware responses that decouple how an error is triggered from how it is formatted for the client.