javascript 37 lines · 7 steps

Async error handling in Express routes

A small wrapper forwards rejected promises to Express's error middleware so async route handlers stay clean.

Explained by highlit
1const asyncHandler = (fn) => (req, res, next) => {
2 Promise.resolve(fn(req, res, next)).catch(next);
3};
4 
5const router = require('express').Router();
6const { User, Order } = require('../models');
7const { NotFoundError } = require('../errors');
8 
9router.get('/users/:id/orders', asyncHandler(async (req, res) => {
10 const user = await User.findByPk(req.params.id);
11 if (!user) {
12 throw new NotFoundError(`User ${req.params.id} not found`);
13 }
14 
15 const orders = await Order.findAll({
16 where: { userId: user.id, status: req.query.status ?? 'active' },
17 order: [['createdAt', 'DESC']],
18 limit: 50,
19 });
20 
21 res.json({
22 user: { id: user.id, email: user.email },
23 orders: orders.map((o) => o.toJSON()),
24 });
25}));
26 
27router.post('/users/:id/orders', asyncHandler(async (req, res) => {
28 const order = await Order.create({
29 userId: req.params.id,
30 items: req.body.items,
31 total: req.body.total,
32 });
33 
34 res.status(201).json(order);
35}));
36 
37module.exports = router;
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Express doesn't catch rejected promises from async handlers, so an unhandled rejection silently hangs the request unless you forward it to next.
  2. 2A tiny higher-order wrapper removes repetitive try/catch from every route while centralizing error handling.
  3. 3Throwing typed errors inside handlers lets one error middleware translate them into consistent HTTP responses.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Async error handling in Express routes — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code