Error Handling & Debugging
Robust error handling separates production-ready Node.js apps from hobby projects. This lesson covers async error propagation, custom error classes, global handlers, and debugging techniques.
1 Custom Error Classes
JavaScript — Custom Errors
// Base application error
class AppError extends Error {
constructor(message, statusCode = 500) {
super(message);
this.statusCode = statusCode;
this.isOperational = true; // vs programmer errors
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); }
}
class UnauthorizedError extends AppError {
constructor() { super('Authentication required', 401); }
}
// Usage in route handlers
async function getUser(req, res) {
const user = await User.findById(req.params.id);
if (!user) throw new NotFoundError('User'); // automatically caught
res.json(user);
}
2 Global Error Handler & Async Wrapper
JavaScript — Error Middleware
// Async wrapper to avoid try/catch in every route
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Use it on routes
app.get('/users/:id', asyncHandler(getUser));
// Global error-handling middleware (must be last!)
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
const message = err.isOperational ? err.message : 'Internal server error';
if (process.env.NODE_ENV === 'development') {
res.status(statusCode).json({ error: message, stack: err.stack });
} else {
res.status(statusCode).json({ error: message });
}
});
// Handle uncaught exceptions and rejections
process.on('uncaughtException', err => {
console.error('UNCAUGHT EXCEPTION:', err);
process.exit(1);
});
process.on('unhandledRejection', (reason) => {
console.error('UNHANDLED REJECTION:', reason);
process.exit(1);
});
3 Code Challenge
Challenge: Install and configure the
winston logging library to write error logs to a logs/error.log file and info logs to the console. Replace all console.error() calls in your app with the logger.