Express.js: Building REST APIsLesson 4.4
Structured error handling in Express APIs
custom AppError class, operational vs programmer errors, async error wrapper, error middleware, HTTP status codes, error response format, 404 handler
A Custom Error Class Carries HTTP Status
Create one error class for all known API errors so your error middleware can format them consistently.
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
}
}Async Error Wrapper
Express 4 does not catch errors thrown inside async route handlers by default. Wrap them:
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 AppError('User not found', 404);
res.json(user);
}));Express 5 handles async errors natively — no wrapper needed.
Global Error Handler
app.use('*', (req, res) => res.status(404).json({ error: 'Route not found' }));
app.use((err, req, res, next) => {
if (err.isOperational) {
return res.status(err.statusCode).json({ error: err.message });
}
console.error('UNHANDLED:', err);
res.status(500).json({ error: 'Something went wrong' });
});