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
rust
use axum::{ http::StatusCode, response::IntoResponse, routing::get,
Serving an SPA and API with Axum
routing
static-files
spa-fallback
Intermediate
7 steps
go
func RecoveryHandler(logger *zap.Logger) gin.HandlerFunc { return gin.CustomRecovery(func(c *gin.Context, recovered any) { var brokenPipe bool if ne, ok := recovered.(*net.OpError); ok {
A panic-recovery middleware in Gin
middleware
panic-recovery
structured-logging
Intermediate
6 steps
javascript
const STORAGE_KEY = "app_state"; const CURRENT_VERSION = 3; const migrations = {
Versioned state migrations in localStorage
migrations
persistence
versioning
Intermediate
9 steps
python
from flask import Blueprint, jsonify, request, abort from .models import Article, db from .schemas import article_schema, articles_schema
Building a REST articles API with Flask Blueprints
rest-api
blueprints
serialization
Intermediate
7 steps
java
@RestController @RequestMapping("/api/users") public class UserController {
Bean Validation in a Spring REST controller
validation
rest-api
exception-handling
Intermediate
9 steps
go
package server import ( "context"
Composing HTTP middleware in Go
middleware
http-handlers
closures
Intermediate
8 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.