koa-compose
Composes an array of Koa middleware into a single async function with built-in safety checks.
Repository Health
Technical Analysis
koa-compose is the tiny, dependency-free utility that turns an array of Koa middleware functions into a single composed async middleware, implementing the recursive “onion” dispatch model that Koa’s entire request-handling pipeline is built on. It flattens nested middleware arrays, validates that every entry is a function, and wraps the composed dispatch in guards that throw when a middleware calls next() more than once or resolves before its downstream handlers finish.
At under two kilobytes of source, it ships as a direct dependency of Koa itself and is pulled in transitively by dozens of routers, frameworks, and testing utilities built on top of Koa, making it one of the most widely executed pieces of code in the Node.js middleware ecosystem despite almost nobody installing it by name.
What You Get
- Single compose() export - one function that flattens and composes an array (or nested arrays) of middleware into a single async middleware
- Runtime safety guards - throws descriptive errors when a middleware calls next() twice or resolves before its downstream chain completes
- Production fast path - composeSlim skips the extra safety checks when NODE_ENV=production for lower per-request overhead
- Zero runtime dependencies - the whole implementation is a single file with no third-party packages to audit or update
Common Use Cases
- Building a custom Koa-compatible framework or router that needs to compose its own middleware stack
- Grouping several route-specific middleware into one composed handler before mounting it on a router
- Writing unit tests that exercise a middleware chain’s dispatch order directly, without booting an HTTP server
Under The Hood
Architecture koa-compose is a single-file, dependency-free module built around one exported function, compose(middleware), that flattens its input with Array.prototype.flat(), validates every entry is a function, and returns a closure implementing a recursive dispatch(i) chain — Koa’s onion model, where each middleware calls next() to yield control downstream and resumes when next() resolves. Two code paths exist side by side: a full dispatch loop used in development that wraps next in a proxy tracking whether it was called more than once or resolved before its downstream chain finished (throwing descriptive errors in either case), and a leaner composeSlim variant that skips those guards entirely when NODE_ENV=production. Because virtually every Koa-based framework and router imports compose() directly, changing its signature or dispatch semantics would ripple through the entire downstream ecosystem, which is why the module’s public surface has stayed essentially unchanged since its early releases.
Tech Stack The package is plain JavaScript with no runtime dependencies declared in package.json; type information for consumers comes from a single JSDoc @typedef import of Koa’s Middleware type rather than a full TypeScript build step. Its devDependencies are limited to koa (used only to resolve that type reference and in tests), the standard linter with snazzy for formatted output, and c8 for coverage reporting layered on Node’s own built-in test runner (node —test) — there is no bundler, transpiler, or build tool in the pipeline since the published artifact is the same single index.js file that lives in the repo.
Code Quality Tests live in test/test.js and use Node’s built-in node:test module (describe/it/beforeEach) with plain assert, covering base dispatch ordering, double-invocation of next(), premature-resolution detection, and repeated calls to the same composed function against different contexts. Error handling is explicit: invalid non-function entries and both categories of dispatch misuse throw typed Error/TypeError instances with actionable messages rather than failing silently. Naming and formatting are enforced by the zero-config standard linter, and a GitHub Actions workflow runs the test suite and lint check on every push, though there are no static type checks beyond the JSDoc annotations.
API Design The public API is intentionally minimal: a single default export, compose(…middleware), accepting either a flat list of functions or nested arrays of them, with no configuration object, options, or class instantiation required. Getting started is a one-line require and call — there is no boilerplate, and the same composed function can be reused across requests since it closes only over the middleware array, not over any request-specific state. The tradeoff for that minimalism is a very thin README with a single API line and no worked examples beyond the type signature, leaving newcomers to read the source or Koa’s own docs to understand the onion-model semantics.
Used by 3 apps in this directory
Logto
Authentication
Open-source auth infrastructure for SaaS and AI apps with OIDC, SAML, and RBAC
NocoBase
No Code Platforms · Low Code Platforms
Open-source AI + no-code platform that lets coding agents and people collaborate to build business systems fast on proven infrastructure.
strapi
CMS
Open-source headless CMS that auto-generates REST and GraphQL APIs from your content models, with a fully customizable admin panel you control.