Learn / Frameworks / Express / Async Errors and Central Handlers

Express · Lesson 6 of 15

Async Errors and Central Handlers

Wrap async routes, throw http-errors and keep a single error middleware.

  • Intermediate
  • 14 min read
  • 3 objectives

Before this lessonLesson 5: Validation with Zod

What you will learn

  • Forward async errors
  • Throw an HttpError
  • Hide 500 details

Your Progress

0 of 15 lessons 0%

  • Lessons0 / 15
  • Completed0
  • Est. time left~ 4 hours

Create a free account to keep your progress on every device.

In Express 4, a rejected promise inside an async handler never reaches your error middleware unless you forward it. One wrapper plus one handler keeps that consistent.

Wrap async routes

const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);

app.get("/tasks/:id", wrap(async (req, res) => {
  const task = await db.task.find(req.params.id);
  if (!task) {
    const err = new Error("Task not found");
    err.status = 404;
    throw err;
  }
  res.json(task);
}));

Express 5 does this for you. Until you are on 5, the wrapper (or express-async-errors) is required.

The error middleware

app.use((err, req, res, next) => {
  const status = err.status || 500;
  if (status >= 500) console.error({ err, requestId: req.requestId });
  res.status(status).json({
    error: status >= 500 ? "Internal Server Error" : err.message,
  });
});

http-errors

import createError from "http-errors";
throw createError(409, "Email already registered");
Up next · Lesson 7Sessions, Cookies and CSRFServer-side sessions vs JWT, cookie flags and CSRF for cookie-based auth.