Express Error Classification
Error handling is a critical aspect of building robust Express applications. Properly classifying errors helps you respond appropriately to different issues that may arise during application execution. In this guide, we'll explore how to classify errors in Express and implement effective error handling strategies.
Introduction to Error Classification
When building Express applications, you'll encounter various types of errors - from syntax errors and runtime exceptions to validation failures and network issues. Classifying these errors helps you:
- Provide appropriate feedback to users
- Log relevant information for debugging
- Maintain security by controlling error details exposed to clients
- Implement specific handling strategies for different error types
Let's dive into the common error types in Express applications and how to handle them effectively.
Common Types of Errors in Express
1. Operational Errors
Operational errors represent runtime problems that are expected and can be handled gracefully. These include:
- Invalid user input
- Failed API requests to external services
- Database connection issues
- Resource not found (404 errors)
- Authentication failures
2. Programming Errors
These are bugs in your code that should be fixed during development:
- Syntax errors
- TypeError (trying to access properties of undefined)
- ReferenceError (using variables that don't exist)
- Logic errors (incorrect algorithm implementation)
3. HTTP Errors
Express applications commonly use HTTP status codes to communicate error states:
Status Code | Category | Description |
---|---|---|
400-499 | Client Errors | Issues caused by client requests |
500-599 | Server Errors | Issues on the server side |
Creating Custom Error Classes
JavaScript allows you to extend the built-in Error
class to create custom error types for better classification:
// Custom error classes
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
// Capture stack trace
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
}
}
class ValidationError extends AppError {
constructor(message) {
super(message, 400);
}
}
Implementing Error Classification
Let's see how to implement error classification in an Express application:
const express = require('express');
const app = express();
// Custom error classes (from previous example)
// ... AppError, NotFoundError, ValidationError classes here
// Sample route that might generate errors
app.get('/users/:id', async (req, res, next) => {
try {
const userId = req.params.id;
// Validate input
if (!userId || isNaN(parseInt(userId))) {
throw new ValidationError('Invalid user ID format');
}
// Simulate database query
const user = await findUser(userId);
if (!user) {
throw new NotFoundError('User');
}
res.json(user);
} catch (err) {
next(err); // Pass errors to the error handler
}
});
// Mock database function
async function findUser(id) {
// Simulate database lookup
return id === '1' ? { id: 1, name: 'John' } : null;
}
// Central error handler
app.use((err, req, res, next) => {
// Determine if this is an operational error or programming error
const statusCode = err.statusCode || 500;
const errorResponse = {
status: statusCode >= 500 ? 'error' : 'fail',
message: statusCode >= 500 && process.env.NODE_ENV === 'production'
? 'Something went wrong'
: err.message
};
// For development, include the stack trace
if (process.env.NODE_ENV === 'development') {
errorResponse.stack = err.stack;
}
// Log error for server-side debugging
if (statusCode >= 500) {
console.error(`SERVER ERROR: ${err.message}`, err.stack);
}
res.status(statusCode).json(errorResponse);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Example: Testing Different Error Scenarios
Let's see how our error classification works in practice:
Scenario 1: Valid Request
Request:
GET /users/1
Response:
{
"id": 1,
"name": "John"
}
Scenario 2: User Not Found
Request:
GET /users/999
Response:
{
"status": "fail",
"message": "User not found"
}
Scenario 3: Invalid Input
Request:
GET /users/abc
Response:
{
"status": "fail",
"message": "Invalid user ID format"
}
Categorizing Errors with HTTP Status Codes
It's important to use appropriate HTTP status codes for different error types:
- 400 Bad Request: Invalid input, missing required fields
- 401 Unauthorized: Authentication required
- 403 Forbidden: Authenticated but not authorized
- 404 Not Found: Resource doesn't exist
- 409 Conflict: Request conflicts with the current state
- 422 Unprocessable Entity: Validation errors
- 500 Internal Server Error: Unexpected server errors
- 503 Service Unavailable: Server temporarily unable to handle requests
Real-World Example: API with Error Classification
Here's a more comprehensive example of a REST API endpoint with error classification:
const express = require('express');
const router = express.Router();
// User registration endpoint
router.post('/register', async (req, res, next) => {
try {
const { email, password, name } = req.body;
// Input validation
if (!email || !password || !name) {
throw new ValidationError('Email, password and name are required');
}
if (password.length < 8) {
throw new ValidationError('Password must be at least 8 characters');
}
// Check if user already exists
const existingUser = await User.findOne({ email });
if (existingUser) {
const error = new AppError('User with this email already exists', 409);
return next(error);
}
// Try to save user
try {
const user = new User({ email, password, name });
await user.save();
res.status(201).json({
status: 'success',
message: 'User registered successfully'
});
} catch (dbError) {
// Database errors
const error = new AppError('Database operation failed', 500);
error.originalError = dbError;
return next(error);
}
} catch (err) {
next(err);
}
});
module.exports = router;
Best Practices for Error Classification
- Create a centralized error handler to process all errors consistently
- Use custom error classes for better organization and specific handling
- Hide technical details in production responses
- Log errors comprehensively for debugging
- Provide meaningful error messages for client errors
- Use appropriate HTTP status codes to indicate the nature of the error
- Include error IDs to correlate client errors with server logs
Handling Async Errors
Express doesn't catch errors in asynchronous code by default. Use these approaches:
1. Try/Catch with async/await
app.get('/users', async (req, res, next) => {
try {
const users = await User.find({});
res.json(users);
} catch (err) {
next(err);
}
});
2. Promise Chaining
app.get('/users', (req, res, next) => {
User.find({})
.then(users => res.json(users))
.catch(err => next(err));
});
3. Error Catching Middleware Wrapper
// Utility to wrap async functions
const catchAsync = fn => {
return (req, res, next) => {
fn(req, res, next).catch(next);
};
};
// Usage
app.get('/users', catchAsync(async (req, res) => {
const users = await User.find({});
res.json(users);
}));
Summary
Proper error classification in Express applications is essential for:
- Providing appropriate responses to clients
- Simplifying debugging and maintenance
- Enhancing application security
- Improving the user experience
By categorizing errors into operational, programming, and HTTP errors, you can implement targeted handling strategies for each type. Using custom error classes and a centralized error handler ensures consistent error responses throughout your application.
Additional Resources
Exercises
- Create a custom error hierarchy for an e-commerce application with specific error types for inventory, payment, and shipping issues.
- Implement a middleware that logs errors to a file with different log levels based on error types.
- Extend the error handler to support internationalization of error messages.
- Create a utility function that converts database errors into appropriate application errors with user-friendly messages.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)