app-builder is a JavaScript library for creating robust and composable asynchronous middleware pipelines, following the 'onion' model commonly seen in frameworks like Koa. It leverages Promises to enable clear and sequential execution of middleware functions, each capable of performing operations before and after subsequent middleware in the stack. The current stable version is 7.0.4. While a specific release cadence isn't explicitly stated, the active GitHub repository suggests ongoing maintenance. Its key differentiator lies in its simplicity and functional approach to middleware composition, providing a lightweight alternative to larger frameworks for building request-response flows or general data processing pipelines. It ships with TypeScript types, ensuring a better developer experience in TypeScript projects.
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
compose
✓ import { compose } from 'app-builder'
✗ const { compose } = require('app-builder')
The library primarily uses ES Modules. While CommonJS might work in some setups, ESM is the recommended and modern way to import.
Demonstrates how to compose asynchronous middleware functions into a pipeline, including logging, core logic, and robust error handling, showcasing both successful and failing scenarios.
import { compose } from 'app-builder';
// Define a simple logger middleware that logs entry and exit
const loggerMiddleware = async (ctx, next) => {
console.log(`Entering middleware: ${ctx.name}`);
await next(); // This is crucial to pass control to the next middleware
console.log(`Exiting middleware: ${ctx.name}`);
};
// Define a core logic middleware that modifies the context
const coreLogicMiddleware = async (ctx, next) => {
ctx.data = 'Processed data';
console.log(`Core logic executed, data: ${ctx.data}`);
await next();
};
// Define an error handling middleware to gracefully catch exceptions
const errorHandlingMiddleware = async (ctx, next) => {
try {
await next();
} catch (error) {
console.error(`An error occurred: ${error.message}`);
ctx.error = error.message; // Store error in context
}
};
// Compose the middleware pipeline
const app = compose(
loggerMiddleware, // First, logs entry
errorHandlingMiddleware, // Catches errors from subsequent middleware
coreLogicMiddleware, // Performs core data processing
async (ctx, next) => {
// This is an inline middleware demonstrating further processing
ctx.status = 'completed';
console.log(`Final status set to: ${ctx.status}`);
await next(); // Even if it's the last, always call next()
}
);
// Define an initial context object
const initialContext = { name: 'MyPipeline', data: null, status: null, error: null };
// Execute the pipeline
app(initialContext)
.then(() => {
console.log('Pipeline finished. Final context:', initialContext);
})
.catch(err => {
console.error('Unhandled pipeline error (should not happen with errorHandlingMiddleware):', err);
});
// Example with an intentional error to show error handling
const failingApp = compose(
errorHandlingMiddleware, // This will catch the error
async (ctx, next) => {
console.log('Failing middleware attempting to run...');
throw new Error('Something went wrong in the pipeline!');
}
);
const failingContext = { name: 'FailingPipeline', error: null };
failingApp(failingContext)
.then(() => {
console.log('Failing pipeline finished. Final context:', failingContext);
})
.catch(err => {
console.error('Unhandled error in failing pipeline execution (should not happen):', err);
});
Errors
Common errors & fixes
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing an error inside an async function without a catch block, or by rejecting a promise which was not handled with .catch().
An asynchronous middleware function threw an error, and no upstream middleware in the pipeline or the final `.catch()` handler on the composed app caught it.
fixWrap `await next()` calls in a `try/catch` block within your middleware, or ensure an error-handling middleware is correctly positioned at the beginning of your pipeline to catch errors from subsequent middleware.
TypeError: next is not a function
A function passed into `compose` did not correctly receive `(ctx, next)` arguments, or `next()` was called outside its scope or with incorrect context.
fixEnsure all functions passed to `compose` adhere to the `async (ctx, next)` signature, even if `next` is not used. Verify the order of arguments.
My outer middleware log appeared before my inner middleware log, but it should have been after!
The `await` keyword was omitted when calling `next()` in an `async` middleware function, causing the outer middleware to proceed before the inner middleware completed.
fixAlways use `await next()` in `async` middleware functions to ensure the 'onion' model's control flow is maintained and execution returns to the current middleware after the inner stack is complete.
Audit
Dependencies
No dependency data recorded yet.