2 · Data and boundaries · 30 MIN
Express async failures and final error handling
An error handler should translate failures without exposing internals.
Express 5 forwards rejected promises from async route handlers to error middleware. The error handler has four parameters and belongs after normal routes. If headers have already been sent, delegate to the default handler rather than trying to send a new JSON response. A public error message can stay stable while internal logs record a safe correlation ID. Distinguish expected input or missing-resource errors from unexpected failures instead of labeling every failure as a client mistake.
Verify the async rejection reaches the handler.
Read the example
import express from 'express';
const app=express();
app.get('/demo',async(req,res)=>{throw new Error('Simulated upstream failure')});
app.use((error,req,res,next)=>{
if(res.headersSent)return next(error);
res.status(503).json({error:'Service temporarily unavailable'});
});
app.listen(3000,'127.0.0.1');Check the expected output
GET /demo returns 503 with a safe JSON message rather than an unhandled promise rejection.
Your challenge
Define distinct expected input and dependency error types, map them to appropriate status codes and test each mapping.
Solution cost: O(1) error mapping in this example. time · O(1) response payload. space
Common trap
Express 4 requires different async forwarding; this lesson explicitly targets Express 5.
Study the project implementation
import express from 'express';
const app=express();
app.get('/demo',async(req,res)=>{throw new Error('Simulated upstream failure')});
app.use((error,req,res,next)=>{
if(res.headersSent)return next(error);
res.status(503).json({error:'Service temporarily unavailable'});
});
app.listen(3000,'127.0.0.1');Further reading: Official documentation
Next lesson: Nest modules, controllers and providers →