How to handle errors in Express
Proper error handling is crucial for building robust Express applications that gracefully handle failures and provide meaningful responses to clients. As the creator of CoreUI with over 25 years of backend development experience, I’ve implemented error handling systems across numerous production APIs. The most effective approach is using Express’s built-in error handling middleware combined with custom error classes for different error types. This ensures consistent error responses and proper logging while preventing application crashes.
Use Express error handling middleware with custom error classes to catch and process all application errors consistently.
const express = require('express')
const app = express()
// Custom error class
class AppError extends Error {
constructor(message, statusCode) {
super(message)
this.statusCode = statusCode
this.isOperational = true
}
}
// Route that might throw an error
app.get('/users/:id', async (req, res, next) => {
try {
const userId = req.params.id
if (!userId) {
throw new AppError('User ID is required', 400)
}
// Simulate database call that might fail
const user = await getUserById(userId)
if (!user) {
throw new AppError('User not found', 404)
}
res.json(user)
} catch (error) {
next(error) // Pass error to error handler
}
})
// Global error handling middleware (must be last)
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500
const message = err.isOperational ? err.message : 'Something went wrong!'
console.error('Error:', err)
res.status(statusCode).json({
status: 'error',
message: message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
})
})
This error handling setup uses a custom AppError class to distinguish between operational errors (client mistakes) and programming errors. The route handler catches errors and passes them to the error middleware using next(error). The global error middleware provides consistent error responses with appropriate status codes and includes stack traces only in development.
Best Practice Note:
This error handling pattern is used throughout CoreUI’s Node.js templates for reliable, production-ready APIs. Always use try-catch blocks for async operations, create specific error types for different scenarios, and log errors for monitoring while sending safe messages to clients.



