express-async-handler

A tiny wrapper that catches async errors in Express route handlers and forwards them to next().

Library
npm
v1.2.0
581stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
35/100Needs Attention
Development Activity0
Maintenance0
Community52
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
48/100Fair
Architecture40
Code Quality60
Innovation65
Learning Curve25

express-async-handler is a small middleware utility for Express applications that removes the boilerplate of wrapping every async route handler in a try/catch block. It wraps a handler function so that any rejected promise — whether thrown explicitly or produced by an awaited call — is automatically caught and passed to Express’s error-handling middleware via next().

Without it, developers writing async route handlers in Express (which does not natively await promises returned from handlers) must manually chain .catch(next) or wrap logic in try/catch on every route. This package reduces that pattern to a single function call around the handler, keeping route definitions concise while preserving Express’s existing error-handling flow.

What You Get

  • A single higher-order function that wraps any async (or sync) Express handler
  • Automatic promise rejection catching, forwarded to next() for Express’s standard error pipeline
  • Support for extra arguments passed through to the wrapped handler beyond the usual (req, res, next)
  • Bundled TypeScript type declarations (index.d.ts) for typed Express handler signatures
  • Zero runtime dependencies — a single small file

Common Use Cases

  • Wrapping Express route handlers that use async/await to query a database or call an external API
  • Ensuring unhandled promise rejections in controllers reach centralized Express error-handling middleware
  • Reducing repetitive try/catch blocks across many route files in an Express API
  • Wrapping custom middleware functions that perform async work before calling next()

Under The Hood

Architecture The entire package is a single exported function, asyncUtil (aliased as expressAsyncHandler), that takes a handler function and returns a new function matching Express’s middleware signature. Internally it invokes the wrapped handler, resolves its return value with Promise.resolve() (so both sync and async handlers work uniformly), and attaches a .catch(next) to forward any rejection to Express’s error-handling chain. There is no internal module structure, no state, and no configuration surface — it is a pure, stateless higher-order function with a single well-defined responsibility.

Tech Stack Written in plain CommonJS JavaScript with no runtime dependencies at all. The package ships a hand-written index.d.ts TypeScript declaration file that types the wrapper against express.RequestHandler and express-serve-static-core, giving TypeScript consumers full generic parameter inference (route params, response body, request body, query) without the package itself being written in TypeScript. Development tooling is mocha for the test runner, chai/sinon/sinon-chai for assertions and spies, and a legacy Travis CI config targeting an old Node.js version.

Code Quality The test suite (test.js) uses mocha with chai and sinon, covering the main behavioral branches directly: synchronous throws, async rejections, forwarding arguments to next(), passing additional arguments through to the handler, non-async functions, and even a manually constructed “thenable” to verify the wrapper doesn’t assume native Promise internals. There is no linter or formatter configuration in the repo, and no modern CI workflow (the .travis.yml is stale, referencing Node 8), but the tests themselves are thoughtful and exercise real edge cases for a package this small.

API Design The public API is a single function taking a single argument, which is about as low-friction as an Express integration can get: wrap a handler in the export and it behaves like any other Express middleware function. The TypeScript declaration preserves full generic typing for request/response bodies without any additional annotations from the consumer. There is no configuration, no separate initialization step, and the README documents the exact problem it solves with a clear before/after code comparison.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search