Express.js Error Handling
Implement robust error handling in Express.js with custom error classes, async error handling, and production-ready error responses.
Overview
Proper error handling is crucial for building reliable Express.js applications. Express provides mechanisms for catching and handling errors both synchronously and asynchronously.
Synchronous Errors
Express automatically catches synchronous errors thrown in route handlers and middleware. These errors are passed to the error-handling middleware.
Asynchronous Errors
For async operations, you need to explicitly pass errors to next() or use async/await with proper try-catch blocks.
Error-Handling Middleware
Error-handling middleware has four parameters: err, req, res, next. Express recognizes it as error-handling middleware by this signature.
Custom Error Classes
Creating custom error classes helps organize different types of errors and allows for consistent error responses.
Production vs Development
In development, you might want detailed error information. In production, hide implementation details and show user-friendly messages.
Code Examples
Custom Error Class
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Usage in routes
app.get('/user/:id', async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) {
return next(new AppError('User not found', 404));
}
res.json(user);
});Async Error Wrapper
// Wrapper for async route handlers
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);
}));
// Errors are automatically passed to error handlerGlobal Error Handler
const globalErrorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
if (process.env.NODE_ENV === 'development') {
res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack
});
} else {
// Production: don't leak error details
if (err.isOperational) {
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
} else {
console.error('ERROR:', err);
res.status(500).json({
status: 'error',
message: 'Something went wrong'
});
}
}
};
module.exports = globalErrorHandler;Frequently Asked Questions
How do I handle errors in async middleware?
Should I handle all errors the same way?
How do I handle uncaught exceptions and unhandled rejections?
Need expert help with Express.js?
Our team at Slashdev.io builds production-ready Express.js applications.