{"id":17496,"library":"app-builder","title":"Composable Middleware Builder","description":"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.","status":"active","version":"7.0.4","language":"javascript","source_language":"en","source_url":"https://github.com/calebboyd/app-builder","tags":["javascript","middleware","functional","composable","composition","pipeline","onion","async","compose","typescript"],"install":[{"cmd":"npm install app-builder","lang":"bash","label":"npm"},{"cmd":"yarn add app-builder","lang":"bash","label":"yarn"},{"cmd":"pnpm add app-builder","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The library primarily uses ES Modules. While CommonJS might work in some setups, ESM is the recommended and modern way to import.","wrong":"const { compose } = require('app-builder')","symbol":"compose","correct":"import { compose } from 'app-builder'"}],"quickstart":{"code":"import { compose } from 'app-builder';\n\n// Define a simple logger middleware that logs entry and exit\nconst loggerMiddleware = async (ctx, next) => {\n  console.log(`Entering middleware: ${ctx.name}`);\n  await next(); // This is crucial to pass control to the next middleware\n  console.log(`Exiting middleware: ${ctx.name}`);\n};\n\n// Define a core logic middleware that modifies the context\nconst coreLogicMiddleware = async (ctx, next) => {\n  ctx.data = 'Processed data';\n  console.log(`Core logic executed, data: ${ctx.data}`);\n  await next();\n};\n\n// Define an error handling middleware to gracefully catch exceptions\nconst errorHandlingMiddleware = async (ctx, next) => {\n  try {\n    await next();\n  } catch (error) {\n    console.error(`An error occurred: ${error.message}`);\n    ctx.error = error.message; // Store error in context\n  }\n};\n\n// Compose the middleware pipeline\nconst app = compose(\n  loggerMiddleware, // First, logs entry\n  errorHandlingMiddleware, // Catches errors from subsequent middleware\n  coreLogicMiddleware, // Performs core data processing\n  async (ctx, next) => {\n    // This is an inline middleware demonstrating further processing\n    ctx.status = 'completed';\n    console.log(`Final status set to: ${ctx.status}`);\n    await next(); // Even if it's the last, always call next()\n  }\n);\n\n// Define an initial context object\nconst initialContext = { name: 'MyPipeline', data: null, status: null, error: null };\n\n// Execute the pipeline\napp(initialContext)\n  .then(() => {\n    console.log('Pipeline finished. Final context:', initialContext);\n  })\n  .catch(err => {\n    console.error('Unhandled pipeline error (should not happen with errorHandlingMiddleware):', err);\n  });\n\n// Example with an intentional error to show error handling\nconst failingApp = compose(\n  errorHandlingMiddleware, // This will catch the error\n  async (ctx, next) => {\n    console.log('Failing middleware attempting to run...');\n    throw new Error('Something went wrong in the pipeline!');\n  }\n);\n\nconst failingContext = { name: 'FailingPipeline', error: null };\nfailingApp(failingContext)\n  .then(() => {\n    console.log('Failing pipeline finished. Final context:', failingContext);\n  })\n  .catch(err => {\n    console.error('Unhandled error in failing pipeline execution (should not happen):', err);\n  });","lang":"typescript","description":"Demonstrates how to compose asynchronous middleware functions into a pipeline, including logging, core logic, and robust error handling, showcasing both successful and failing scenarios."},"warnings":[{"fix":"Always ensure `await next()` is used when calling the next middleware in an `async` function if you expect control to return to the current middleware after the inner stack has completed.","message":"Forgetting to `await next()` within an asynchronous middleware function will cause the outer middleware to complete its 'after' logic immediately, before inner middleware has finished executing. This breaks the intended 'onion' model of execution.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Implement an error-handling middleware that wraps `await next()` in a `try/catch` block, or ensure individual middleware functions handle their own potential errors.","message":"Errors thrown by asynchronous middleware functions will result in unhandled promise rejections if not explicitly caught by a `try/catch` block within the middleware itself or by a dedicated error-handling middleware placed higher in the composition.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Establish clear conventions for context properties and their expected types/values at each stage of the pipeline. Consider using immutable data patterns or a more structured context object where applicable.","message":"While flexible, extensive mutation of the context object across many middleware functions without clear contracts can lead to difficult-to-debug side effects and data flow issues, especially in complex pipelines.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"fix":"Wrap `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.","cause":"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.","error":"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()."},{"fix":"Ensure all functions passed to `compose` adhere to the `async (ctx, next)` signature, even if `next` is not used. Verify the order of arguments.","cause":"A function passed into `compose` did not correctly receive `(ctx, next)` arguments, or `next()` was called outside its scope or with incorrect context.","error":"TypeError: next is not a function"},{"fix":"Always 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.","cause":"The `await` keyword was omitted when calling `next()` in an `async` middleware function, causing the outer middleware to proceed before the inner middleware completed.","error":"My outer middleware log appeared before my inner middleware log, but it should have been after!"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}