Handling Async Errors

Errors thrown in async handlers must be passed to next() or Express will not catch them.

SyntaxasyncHandler(fn)

Express automatically catches errors thrown in synchronous handlers, but not those from rejected promises in async handlers. If you forget, the request hangs.

Wrap async handlers so any rejection is forwarded to next(). A small helper avoids repeating try/catch everywhere. In Express 5 this is handled automatically, but the wrapper is still common.

Example

Example · javascript
const asyncHandler = (fn) => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

app.get('/posts', asyncHandler(async (req, res) => {
  const posts = await db.getPosts(); // may reject
  res.json(posts);
}));

// Rejections now reach the error handler
app.use((err, req, res, next) => {
  res.status(500).json({ error: err.message });
});

When to use it

  • A route that queries a database with async/await wraps the call in try/catch and passes any rejection to next(err) for centralised handling.
  • A team uses an asyncHandler wrapper utility so they never have to write try/catch in every async route handler.
  • In Express 5, unhandled promise rejections inside route handlers are automatically forwarded to the next error middleware without any wrapper.

More examples

try/catch with next(err)

Wraps the async database call in try/catch and calls next(err) so the global error middleware handles unexpected failures.

Example · js
app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await db.users.findById(req.params.id);
    if (!user) return res.status(404).json({ error: 'Not found' });
    res.json(user);
  } catch (err) {
    next(err); // forwards to error middleware
  }
});

asyncHandler utility wrapper

Defines a one-line asyncHandler that catches any rejected promise and passes it to next, eliminating repetitive try/catch blocks.

Example · js
const asyncHandler = fn => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

app.get('/posts', asyncHandler(async (req, res) => {
  const posts = await db.posts.findAll();
  res.json(posts);
}));

app.get('/posts/:id', asyncHandler(async (req, res) => {
  const post = await db.posts.findById(req.params.id);
  if (!post) return res.status(404).json({ error: 'Not found' });
  res.json(post);
}));

Express 5 automatic async errors

In Express 5, async route handlers that reject or throw automatically call next(err) — no try/catch or wrapper required.

Example · js
// Express 5: rejected promises are forwarded automatically
app.get('/items', async (req, res) => {
  const items = await db.items.findAll(); // if this rejects, error middleware is called
  res.json(items);
});

// Error middleware still needed to handle the error
app.use((err, req, res, next) => {
  res.status(500).json({ error: err.message });
});

Discussion

  • Be the first to comment on this lesson.