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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Express doesn't catch rejected promises from async handlers, so an unhandled rejection silently hangs the request unless you forward it to next.
- 2A tiny higher-order wrapper removes repetitive try/catch from every route while centralizing error handling.
- 3Throwing typed errors inside handlers lets one error middleware translate them into consistent HTTP responses.
Related explainers
javascript
import { unstable_cache, revalidateTag } from 'next/cache' import { db } from '@/lib/db' export const getDashboardStats = unstable_cache(
Caching dashboard stats in Next.js
caching
cache-invalidation
tag-based-revalidation
Intermediate
8 steps
go
package middleware import ( "compress/gzip"
How gzip HTTP middleware works in Go
middleware
compression
object-pooling
Intermediate
7 steps
javascript
import { Component } from 'react'; import { reportError } from './services/telemetry'; export class ErrorBoundary extends Component {
How a React ErrorBoundary works
error-handling
lifecycle-methods
render-props
Intermediate
8 steps
rust
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD}; use base64::{DecodeError, Engine}; pub fn encode_standard(data: &[u8]) -> String {
Base64 encode and decode in Rust
base64
encoding
error-handling
Beginner
7 steps
javascript
const FOCUSABLE = [ 'a[href]', 'button:not([disabled])', 'input:not([disabled])',
How to trap keyboard focus in a dialog
accessibility
dom
event-handling
Intermediate
8 steps
javascript
function generateCalendarGrid(year, month) { const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const daysInMonth = lastDay.getDate();
Building a text calendar in JavaScript
date-handling
grid-layout
modular-arithmetic
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/async-error-handling-in-express-routes-explained-javascript-0d59/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.