🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Implement comprehensive error handling
- Create custom error classes
- Use error handling middleware
- Handle different types of errors
- Implement logging and monitoring
- Create user-friendly error responses
📚 Error Handling Patterns
Custom Error Classes
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message) {
super(message, 400);
}
}
Error Handling Middleware
function errorHandler(err, req, res, next) {
let error = { ...err };
error.message = err.message;
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new AppError(message, 404);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new AppError(message, 400);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message);
error = new AppError(message, 400);
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error'
});
}
🎯 Practice Exercises
Exercise 1: Complete Error Handling System
Create a comprehensive error handling system:
Solution:
const express = require('express');
const mongoose = require('mongoose');
const app = express();
app.use(express.json());
// Custom Error Classes
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message) {
super(message, 400);
}
}
class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 404);
}
}
// Error Handling Middleware
function errorHandler(err, req, res, next) {
let error = { ...err };
error.message = err.message;
// Log error
console.error(err);
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new AppError(message, 404);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new AppError(message, 400);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message);
error = new AppError(message, 400);
}
// JWT errors
if (err.name === 'JsonWebTokenError') {
const message = 'Invalid token';
error = new AppError(message, 401);
}
if (err.name === 'TokenExpiredError') {
const message = 'Token expired';
error = new AppError(message, 401);
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
}
// Async Error Handler
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
// Example route with error handling
app.get('/api/users/:id', asyncHandler(async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) {
return next(new NotFoundError('User'));
}
res.json({
success: true,
data: user
});
}));
// Global error handler (must be last)
app.use(errorHandler);
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
🎯 Key Takeaways
- Custom error classes provide specific error types
- Error middleware handles all errors centrally
- Async error handling catches promise rejections
- Error logging helps with debugging
- User-friendly messages improve experience
Next Lesson: Lesson 10: Mini Project - Simple Blog API