Express-style Middleware Executor
raw JSON →execute-middleware is a JavaScript utility designed for the standalone execution of Express-style middleware logic, outside of a full Express application. It processes both standard middleware (expecting `(req, res, next)`) and error-handling middleware (expecting `(err, req, res, next)`), automatically differentiating between them by the number of arguments. The library supports complex nesting of middleware arrays and correctly handles error propagation: when `next(err)` is called, subsequent normal middleware is skipped until an error handler is encountered. It offers both serial and concurrent execution modes for middleware chains. The current stable version is 1.0.3. While no explicit release cadence is stated, it appears to be a mature, stable utility providing a focused solution for running Express middleware patterns in a more controlled or isolated environment.
Common errors
error TypeError: (intermediate value).serial is not a function ↓
const executeMW = require('execute-middleware');. For ESM, use import executeMW from 'execute-middleware'; or import { serial } from 'execute-middleware';. error TypeError: next is not a function ↓
req, res, and next mock objects/functions as the second, third, and fourth arguments respectively to executeMW.serial() or executeMW.concurrent(). Warnings
gotcha Middleware functions are distinguished by argument count: 3 arguments `(req, res, next)` for normal middleware, and 4 arguments `(err, req, res, next)` for error-handling middleware. Incorrect argument counts will lead to unexpected behavior (e.g., an error handler being treated as normal middleware). ↓
gotcha Calling `next(someValue)` where `someValue` is not an `Error` object will still cause `execute-middleware` to treat it as an error and trigger the error handling flow, skipping subsequent normal middleware. This mirrors Express's behavior. ↓
gotcha Asynchronous operations within middleware (e.g., database calls, API requests) must explicitly call `next()` or `next(err)` upon completion. If `next()` is not called, the middleware chain will hang indefinitely, never progressing to the next step. ↓
Install
npm install execute-middleware yarn add execute-middleware pnpm add execute-middleware Imports
- executeMW wrong
const executeMW = require('execute-middleware');correctimport executeMW from 'execute-middleware'; - serial wrong
import executeMW from 'execute-middleware'; const serial = executeMW.serial;correctimport { serial } from 'execute-middleware'; - concurrent wrong
const { concurrent } = require('execute-middleware');correctimport { concurrent } from 'execute-middleware';
Quickstart
import executeMW from 'execute-middleware';
// Mock Express-like objects for demonstration
const req = {
url: '/',
method: 'GET',
params: {},
query: {},
body: {},
data: {} // For middleware to attach data
};
const res = {
statusCode: 200,
_body: null,
_headers: {},
send: function(data) {
this._body = data;
console.log('Response sent:', data);
},
status: function(code) {
this.statusCode = code;
return this;
},
json: function(data) {
this.send(JSON.stringify(data));
},
set: function(name, value) {
this._headers[name.toLowerCase()] = value;
return this;
}
};
// The final `next` function for the entire chain
const finalNext = (err) => {
if (err) {
console.error('Final next caught an error:', err.message);
if (!res._body) { // Only send if no middleware already sent one
res.status(500).json({ error: err.message || 'Internal Server Error' });
}
} else {
console.log('All middleware in chain completed successfully.');
if (!res._body) { // If no middleware sent a response
res.status(200).send('No response sent by middleware, but chain completed.');
}
}
};
// Some middleware
const SOME_MIDDLEWARE = [
(req, res, next) => {
console.log('Middleware 1: Initializing request data.');
req.data.message = 'Hello from MW1';
next();
},
[ // Nested middleware array
(req, res, next) => {
console.log('Middleware 2.1: Processing:', req.data.message);
req.data.processed = true;
next();
},
(err, req, res, next) => { // This is an error handler, won't be called here in normal flow
console.log('Middleware 2.2 (Error Handler): This should be skipped in normal flow.');
next(err); // Pass error along if called
}
],
(req, res, next) => {
console.log('Middleware 3: Final step, received:', req.data);
// Simulate sending a response
res.status(200).json({ status: 'success', data: req.data });
// Do not call next() here if response is sent, typically ends the chain
}
];
async function runSerialExample() {
console.log('--- Running Serial Middleware Example (Normal Flow) ---');
// Pass in the middleware array, then the usual express (req, res, next) values
await executeMW.serial(SOME_MIDDLEWARE, req, res, finalNext);
console.log('--- Serial Middleware Finished (Normal Flow) ---\n');
// Example with error propagation
const errorReq = { ...req, data: {} }; // Fresh request object
const errorRes = { ...res, _body: null }; // Fresh response object
const ERROR_MIDDLEWARE = [
(req, res, next) => {
console.log('Error MW 1: About to trigger an error.');
// Simulate an asynchronous operation that throws an error
setTimeout(() => {
next(new Error('Validation failed for input!'));
}, 50);
},
(req, res, next) => {
console.log('Error MW 2: This middleware should be skipped because of the error.');
next(); // This next() will not be called as error handler takes over
},
(err, req, res, next) => { // This is an error handler middleware
console.error('Error MW 3 (Error Handler): Caught error:', err.message);
errorRes.status(400).json({ message: 'Request Error', detail: err.message });
next(); // Call next to complete the error handling chain, allowing finalNext to run
}
];
console.log('--- Running Serial Middleware with Error Example ---');
await executeMW.serial(ERROR_MIDDLEWARE, errorReq, errorRes, finalNext);
console.log('--- Serial Middleware Finished (Error Flow) ---');
}
runSerialExample();