{"id":17424,"library":"composition","title":"Async/Await & Generator Middleware Composition","description":"The `composition` package provides a utility for composing asynchronous middleware functions in a manner similar to Koa. It uniquely supports mixing both traditional generator functions (using `yield next`) and modern `async/await` functions within the same middleware stack. Each composed middleware function is expected to return a Promise or handle `next()` correctly to ensure the control flow propagates. The package is currently at version 2.3.0, indicating a stable release focused on robust async control flow. While no explicit release cadence is documented, utilities of this nature tend to be stable with updates driven by significant changes in JavaScript's asynchronous patterns or bug fixes. Its key differentiator is the seamless interoperability between generator-based and promise-based async middleware, making it suitable for projects transitioning or requiring compatibility across different asynchronous programming styles.","status":"active","version":"2.3.0","language":"javascript","source_language":"en","source_url":"https://github.com/thenables/composition","tags":["javascript","co","compose","composition","middleware","decorator","decorators","async","await"],"install":[{"cmd":"npm install composition","lang":"bash","label":"npm"},{"cmd":"yarn add composition","lang":"bash","label":"yarn"},{"cmd":"pnpm add composition","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"The primary export 'compose' is a default export for ESM modules.","wrong":"import { compose } from 'composition';","symbol":"compose","correct":"import compose from 'composition';"},{"note":"For CommonJS environments, 'compose' is the `module.exports` default, not a named export.","wrong":"const { compose } = require('composition');","symbol":"compose","correct":"const compose = require('composition');"},{"note":"For TypeScript users, `MiddlewareFunction` might be available as a type import to define the signature of middleware functions for better type checking, assuming type declarations are provided.","symbol":"MiddlewareFunction","correct":"import type { MiddlewareFunction } from 'composition';"}],"quickstart":{"code":"var compose = require('composition');\n\nvar stack = [];\n\n// Middleware 1: A generator function\nstack.push(function* (next) {\n  console.log('Middleware 1 (generator): Before calling next()');\n  yield next;\n  console.log('Middleware 1 (generator): After next() returned');\n});\n\n// Middleware 2: A regular function returning a Promise\nstack.push(function (next) {\n  console.log('Middleware 2 (promise): Before calling next()');\n  return Promise.resolve('Data from Middleware 2').then((data) => {\n    console.log('Middleware 2 (promise): Received data:', data);\n    return next(); // Ensure next is called and its result is returned as a Promise\n  }).then(() => {\n    console.log('Middleware 2 (promise): After next() returned');\n    return 'Modified by Middleware 2';\n  });\n});\n\n// Middleware 3: An async/await function\nstack.push(async function (next) {\n  console.log('Middleware 3 (async/await): Before calling next()');\n  const result = await next(); // Await the result of the downstream middleware\n  console.log('Middleware 3 (async/await): After next() returned with:', result);\n  return 'Final value from Middleware 3';\n});\n\n// Compose the middleware stack into a single function\nvar fn = compose(stack);\n\n// Invoke the composed function, which returns a Promise\nfn.call({ customContext: 'hello' }).then(function (finalVal) {\n  console.log('Composed function resolved with final value:', finalVal);\n}).catch(function (err) {\n  console.error('Composed function caught an error:', err.stack);\n  process.exit(1);\n});","lang":"javascript","description":"Demonstrates how to compose a stack of mixed generator, promise-returning, and async/await middleware functions, illustrating the control flow, context passing, and handling of return values and errors."},"warnings":[{"fix":"For `async/await` middleware, ensure `await next();` is used. For generator middleware, use `yield next;`. For plain promise-returning functions, ensure `return next().then(...)` or `return Promise.resolve().then(() => next())` is used to propagate control.","message":"Middleware functions must explicitly call `next()` and ensure their return value, or the result of `next()`, is a Promise that is either returned or awaited. Failure to do so will break the middleware chain, preventing subsequent middleware from executing or causing unexpected behavior.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Use Node.js v7.6.0 or higher for native `async/await` support. For environments with limited feature support, transpile code using Babel or a similar tool to ensure compatibility with your target runtime.","message":"Mixing generator functions and `async/await` functions within the same stack requires a Node.js environment that supports both. Older Node.js versions (e.g., prior to v7.6.0) may not support `async/await` natively, and generators themselves require specific syntax support.","severity":"gotcha","affected_versions":"<=7.0.0"},{"fix":"Always pass the desired `this` context to the composed function via `fn.call(yourContext)` or `fn.apply(yourContext)`. If no specific context is required, passing `null` or `{}` is acceptable.","message":"The `this` context for middleware functions is explicitly set when the composed function is invoked (e.g., `fn.call(thisArg)`). If middleware relies on a specific `this` context and it's not provided or is incorrect, it can lead to `TypeError` or unexpected runtime errors.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Review all middleware functions to ensure `next` is correctly invoked and awaited/yielded. Verify that the function signature includes `next` as a parameter if it's being called.","cause":"A middleware function in the stack failed to correctly call `next()`, or the `next` argument was not properly propagated through the middleware chain.","error":"TypeError: next is not a function"},{"fix":"Add a `.catch()` handler to the invocation of the composed function (e.g., `fn().catch(err => console.error(err))`) to gracefully handle any errors that propagate out of the middleware stack.","cause":"A Promise within the middleware chain rejected, and the rejection was not explicitly caught by a `.catch()` block in the composed function's invocation or any preceding middleware.","error":"UnhandledPromiseRejectionWarning: Unhandled promise rejection."},{"fix":"Ensure `await` is only used inside functions declared with the `async` keyword. If using `async/await` in older Node.js versions, ensure you are using Node.js v7.6.0 or newer.","cause":"Attempting to use the `await` keyword outside of an `async` function in an environment that does not support top-level await, or within a non-async middleware function.","error":"SyntaxError: await is only valid in async functions and the top level bodies of modules"}],"ecosystem":"npm","meta_description":null}