Async Express Middleware Wrapper

raw JSON →
1.4.0 verified Thu Apr 23 auth: no javascript maintenance

middleware-async is a utility designed to simplify error handling within asynchronous Express.js or Connect-style middleware. At its stable version 1.4.0, last updated in 2017, it addresses the challenge of correctly propagating errors from `async`/`await` functions or Promises to Express's built-in error handling mechanism. Prior to Express 5.x, unhandled promise rejections in middleware would not automatically be caught by Express and could lead to application crashes. This package provides a wrapper function, `asyncMiddleware`, that catches both synchronous errors and rejected Promises within your middleware, automatically calling `next(err)`. Its release cadence is effectively dormant due to its age and the introduction of native async error handling in modern Express versions. It differentiates itself by offering a straightforward, minimalist solution to avoid repetitive `try...catch` blocks for older Express applications.

error UnhandledPromiseRejectionWarning: A promise was rejected with a non-error value.
cause An asynchronous middleware function returned a rejected Promise, but the rejection was not caught by Express or a wrapper, leading to an unhandled rejection at the process level. This is common in Express 4.x without a wrapper.
fix
Ensure all async Express middleware functions are wrapped with asyncMiddleware (e.g., app.get('/route', asyncMiddleware(myAsyncFunc), ...)) or manually include try...catch blocks or .catch(next) within your async functions.
error TypeError: app.use() requires a middleware function but got a Object
cause This typically occurs when `require('middleware-async')` is used in a way that attempts to access a named export (e.g., `.asyncMiddleware`) when the package's main export is the function itself, or if an ESM default import is used incorrectly in CommonJS.
fix
For CommonJS, import the module directly as a function: const asyncMiddleware = require('middleware-async');. For ESM, use import asyncMiddleware from 'middleware-async';.
breaking Express 5.x introduces native support for async route handlers and middleware, automatically catching unhandled promise rejections and passing them to `next(err)`. This package becomes largely redundant in Express 5.x and newer versions, potentially causing unnecessary overhead or conflicts if used alongside native handling.
fix For new projects or when upgrading to Express 5.x+, consider removing `middleware-async` and writing async middleware directly. Ensure a global error handling middleware is in place for all errors. Example: `app.get('/', async (req, res) => { throw new Error('Broken'); });`
gotcha Without a wrapper like `middleware-async` (or Express 5.x's native support), a rejected Promise in an `async` Express middleware will result in an `UnhandledPromiseRejectionWarning` and can crash your Node.js process, as Express 4.x does not automatically catch async errors.
fix Always wrap async middleware functions using `asyncMiddleware` or explicitly use `try...catch` blocks or `.catch(next)` within each async middleware for Express versions older than 5.x.
deprecated The package has not seen updates since 2017 (version 1.4.0). While functional for its intended purpose with older Express versions, it lacks modern maintenance, features, or explicit ESM support. Consider `express-async-handler` for a more recently maintained alternative for Express 4.x, or upgrade to Express 5.x for native support.
fix For new projects, prefer native async error handling in Express 5.x. For older projects, evaluate `express-async-handler` (`npm i express-async-handler`) as a potentially more robust and maintained alternative if `middleware-async` encounters issues.
npm install middleware-async
yarn add middleware-async
pnpm add middleware-async

Demonstrates wrapping an asynchronous Express middleware with `asyncMiddleware` to catch promise rejections and forward them to Express's error handler, preventing application crashes.

import express from 'express';
import asyncMiddleware from 'middleware-async';

const app = express();

// A simulated asynchronous operation
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.5;
      if (success) {
        resolve({ message: 'Data fetched successfully!' });
      } else {
        reject(new Error('Failed to fetch data.'));
      }
    }, 500);
  });
};

// Define an async middleware that uses fetchData
const myAsyncMiddleware = async (req, res, next) => {
  console.log('Attempting to fetch data...');
  const data = await fetchData(); // This might reject
  req.fetchedData = data;
  next();
};

// Use the asyncMiddleware wrapper
app.get('/data', asyncMiddleware(myAsyncMiddleware), (req, res) => {
  res.json({ status: 'Success', data: req.fetchedData });
});

// Error handling middleware (must be defined last)
app.use((err, req, res, next) => {
  console.error('An error occurred:', err.message);
  res.status(500).json({ status: 'Error', message: 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/data multiple times to see success/failure.');
});