Express Error Handling Patterns That Scale
Default Express error handling sends a bare HTML page to clients. Here's how to build something production-worthy.
Custom Error Classes
Create a hierarchy of error types:
class AppError extends Error {
constructor(message, statusCode, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
}
}
class ValidationError extends AppError {
constructor(errors) {
super('Validation failed', 400);
this.errors = errors;
}
}Async Error Wrapper
Express doesn't catch async errors by default. Wrap your route handlers:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) throw new NotFoundError('User');
res.json(user);
}));Centralized Error Handler
app.use((err, req, res, next) => {
// Log all errors
logger.error({
message: err.message,
stack: err.stack,
path: req.originalUrl,
method: req.method,
});
if (err.isOperational) {
return res.status(err.statusCode).json({
status: 'error',
message: err.message,
...(err.errors && { errors: err.errors }),
});
}
// Programming errors — don't leak details
res.status(500).json({
status: 'error',
message: 'Something went wrong',
});
});Operational vs. Programming Errors
- Operational errors (user not found, invalid input): expected, respond with appropriate status codes
- Programming errors (undefined property access, type errors): unexpected, log the full stack trace and return generic 500
This distinction is critical. Operational errors are normal; programming errors need immediate attention. Bugsly helps by flagging new error types so you can quickly distinguish between the two and prioritize fixes accordingly.
Try Bugsly Free
AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.
Get Started FreeRelated Articles
Django Performance Monitoring Best Practices
Master Django performance monitoring with best practices for query optimization, middleware profiling, caching strategies, and real-time alerting.
Read moreLaravel Error Handling Patterns
Master Laravel error handling with custom exceptions, the Handler class, renderable exceptions, and API error response formatting.
Read moreVue.js Production Best Practices
Essential Vue.js best practices for production apps covering state management, component patterns, performance optimization, and error handling.
Read moreGo Production Best Practices
Essential Go production best practices covering project structure, dependency management, concurrency safety, and deployment strategies.
Read more