Building RESTful APIsLesson 3.5
How to handle async route handlers without crashing Express
async await in Express, unhandled promise rejection, try catch boilerplate, asyncHandler wrapper, process uncaughtException, error propagation with next
Async Error Handling in Express
Express 4 does not catch promise rejections automatically. An unhandled rejection in an async route handler crashes the process or hangs the request.
The problem
// This will crash if fetchData() throws
app.get('/data', async (req, res) => {
const data = await fetchData(); // unhandled rejection
res.json(data);
});Option 1 — try/catch every handler
app.get('/data', async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (err) {
next(err); // pass to error handler
}
});Option 2 — asyncHandler wrapper (DRY)
const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
// Usage — no try/catch needed
app.get('/data', asyncHandler(async (req, res) => {
const data = await fetchData();
res.json(data);
}));
app.post('/users', asyncHandler(async (req, res) => {
const user = await createUser(req.body);
res.status(201).json(user);
}));The asyncHandler wrapper is ~2 lines but eliminates try/catch boilerplate across every async route. Express 5 (released stable) handles this natively — but until your app uses Express 5, this wrapper is essential.
