In this lesson, you will learn how to create a structured error hierarchy that allows your application to categorize failures and respond to them with surgical precision.
Why Standardize?
When you rely solely on generic Error objects, your middleware and global handlers are forced to inspect error messages or status codes via string matching, which is brittle and prone to bugs. By standardizing error types, you turn "chaos" into "data" that your orchestrators can act upon.
Creating the Hierarchy
We start with a base class that extends the native Error object, then extend that for specific domains. This allows you to differentiate between an operational issue (like a validation error) and a critical infrastructure failure.
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message) {
super(message, 400); // Bad Request
}
}
class NetworkError extends AppError {
constructor(message) {
super(message, 503); // Service Unavailable
}
}
The Power of instanceof
With this hierarchy in place, your global error-handling logic stops guessing. Instead of checking if (err.message.includes('timeout')), you use the instanceof operator to apply specific strategies—like retrying a NetworkError or logging a ValidationError without paging an engineer.
try {
await database.save(data);
} catch (err) {
if (err instanceof ValidationError) {
return response.status(400).json({ error: err.message });
}
if (err instanceof NetworkError) {
// Trigger circuit breaker or retry logic
return retryOperation();
}
// Generic fallback for unknown system failures
process.exit(1);
}
By categorizing errors into Validation, Network, and System failures, you ensure that your "Fail Fast" philosophy doesn't become "Crash Often." You now have the granularity to handle transient network blips differently than logic-breaking data corruption, which is essential for building resilient distributed nodes.
Key Takeaway: Use custom error classes to move from inspecting error strings to using type-safe checks, enabling your application to react to failures based on their nature rather than their description.