Async Express/Connect Middleware Wrapper
async-middleware is a utility package designed to simplify error handling for asynchronous middleware functions within web frameworks like Express and Connect. It wraps an `async` or Promise-returning function, automatically catching rejected Promises and forwarding the error to the `next(err)` callback, preventing uncaught Promise rejections from crashing the application. The package is currently at version 1.2.1 and appears to be in a maintenance state, with the last release being in 2017 focusing on minor fixes and dependency cleanups rather than new features. Its key differentiator is its singular focus on robust async error piping for traditional middleware stacks, providing a lightweight alternative to more comprehensive async libraries. It ships with TypeScript types, enhancing developer experience for type-safe applications.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'wrap')
cause Attempting to `require('async-middleware')` directly without accessing the named export `wrap`.fixUse `const { wrap } = require('async-middleware');` for CommonJS or `import { wrap } from 'async-middleware';` for ESM. -
Error: next() called with non-Error object: [object Object]
cause `async-middleware` caught a rejected Promise where the rejected value was not an `Error` object, and the upstream error handler expects `Error` instances.fixEnsure that your Promises consistently reject with `Error` objects. For example, instead of `Promise.reject({ code: 500 })`, use `Promise.reject(new Error('Internal Server Error'))`.
Warnings
- breaking Prior to v1.1.0, `async-middleware` would use `Promise.resolve` on all return values, potentially converting non-Promise values into Promises. Starting with v1.1.0, it strictly only handles `Promise`-like returns and ignores other values. If your middleware explicitly returned non-Promise values that you expected to be 'resolved' into Promises, this behavior changed.
- gotcha Version 1.2.0 dropped the `any-promise` dependency, relying solely on native ES6 Promise types. While this generally improves performance and reduces bundle size, it means environments without native Promise support (e.g., older Node.js or browser versions without polyfills) might require an explicit Promise polyfill if you're using this library.
- gotcha When using TypeScript, earlier versions might have relied on ambient type declarations. Version 1.2.0 moved type definitions to `@types`, which means your `tsconfig.json` should be configured to include `@types` packages correctly.
- gotcha While `async-middleware` catches rejected Promises, it does not provide any default error transformation. If a Promise rejects with a falsy value (e.g., `null` or `undefined`), v1.1.0 and later will `next()` a custom `Error` instance with a generic message, rather than the falsy value itself. Always reject with actual `Error` objects for clearer debugging.
Install
-
npm install async-middleware -
yarn add async-middleware -
pnpm add async-middleware
Imports
- wrap
const wrap = require('async-middleware');import { wrap } from 'async-middleware'; - wrap
const wrap = require('async-middleware').default;const { wrap } = require('async-middleware'); - WrapMiddleware
import type { WrapMiddleware } from 'async-middleware';
Quickstart
import express from 'express';
import { wrap } from 'async-middleware';
const app = express();
// Basic asynchronous middleware that resolves after a delay
app.get('/success', wrap(async (req, res) => {
await new Promise(resolve => setTimeout(resolve, 100));
res.send('Operation successful!');
}));
// Asynchronous middleware that rejects with an error
app.get('/fail', wrap(async (req, res) => {
await new Promise((_, reject) => setTimeout(() => reject(new Error('Something went wrong!')), 50));
// This line will not be reached
res.status(200).send('Should not see this');
}));
// Error handling middleware (must be defined last)
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error('Caught by error handler:', err.message);
res.status(500).send(`Error: ${err.message}`);
});
const PORT = process.env.PORT ?? 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log('Try visiting http://localhost:3000/success and http://localhost:3000/fail');
});