Error-Handling Middleware
Error handlers have four arguments and catch errors passed with next(err).
Syntax
app.use((err, req, res, next) => { ... })Error-handling middleware is special: it takes four arguments, (err, req, res, next). Express recognizes the extra err parameter and only calls it when an error occurs.
To trigger it, call next(err) from anywhere, or throw inside a synchronous handler. Define your error handler last, after all routes.
Example
app.get('/boom', (req, res, next) => {
next(new Error('Something broke'));
});
// Error handler goes last
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).json({ error: err.message });
});When to use it
- A developer registers a single error middleware at the bottom of the app to catch all errors thrown or passed via next(err) and return consistent JSON error responses.
- A team's error middleware logs the stack trace to a monitoring service in production but sends a generic 500 message to the client.
- A validation library throws typed errors that the error middleware inspects to return 400 for validation errors versus 500 for unexpected ones.
More examples
Basic error middleware
An Express error middleware must declare 4 parameters (err, req, res, next) — Express identifies it by its arity.
// Must have exactly 4 parameters
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).json({ error: 'Internal Server Error' });
});Triggering error middleware with next(err)
Shows a route that catches a synchronous error and passes it to next(err), which triggers the error middleware.
app.get('/risky', (req, res, next) => {
try {
throw new Error('Something went wrong');
} catch (err) {
next(err); // hands off to error middleware
}
});
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});Typed error handling
Uses a custom ValidationError class with a status property so the error middleware can set the correct HTTP status.
class ValidationError extends Error {
constructor(msg) { super(msg); this.status = 400; }
}
app.post('/items', (req, res, next) => {
if (!req.body.name) return next(new ValidationError('name is required'));
res.status(201).json({ created: true });
});
app.use((err, req, res, next) => {
res.status(err.status || 500).json({ error: err.message });
});
Discussion