The Koa web framework provides a barebones middleware system that works with Promises on Node.js. It doesn’t have much built in, and it’s necessary to plug in additional middleware for routing and other considerations in the request lifecycle. If we prefer working with RxJS and Observables, and don’t mind writing our own middleware — whether for control or as a practical design exercise — it’s straightforward to put together a wrapper for Koa to run our own reactive middleware pipeline instead of native Koa middleware.
Before getting started, we’ll need Node.js — I’m running
10.x for this project. Add the following scripts and dependencies in a
package.json and install with
npm install (or install via the NPM or Yarn CLI).
tsconfig.json at the project root:
Note: All code will be assumed to be in
src/ relative to the project root.
A lightly opinionated API for defining routes consists of a predicate to match requests against, and a handler to operate on a request. Additionally, we’ll expose a
handleRoute() helper for the server to consume.
At this point, a route can be defined with a matcher for the desired URL. Let’s use
/hello for this example. Writing to the response and then returning an Observable that immediately emits a single element will satisfy the router’s contract and allow the server to continue down its pipeline after processing each request.
Collecting all our routes in one configuration file, we can export a single array to be consumed by our application.
The server is the most complex component of this solution. It initializes a Koa application and handles the full request pipeline using one RxJS subscription per request, run within a single middleware.
Finally, to initialize our application, we can create a server, passing in routes and lifecycle hooks, and telling it where to listen. The listen methd is written to take the same parameters as
Koa#listen, as it delegates to the Koa application instance.
If we have everything together and the start script as defined at the top of the article, with an
npm run start, we should be able to see the running application at
An additional capability to provide here would be extending the routing predicate functionality to extract route params and plumb these through to the handler. With request parsing for URL params and body content, standardized HTTP status handling, and additional middleware hooks, this solution would come together as a practical basis for a Node.js web application. For parts of these capabilities, lower-libraries exist outside the Koa middleware ecosystem that would be straightforward to plug into our reactive middleware pipeline.
Additionally, facading the Koa
Context such that the full
ctx object isn’t the primary API to work with at every stage of the pipeline would be good — although suitable for low-level middleware, route handlers could be provided a more opinionated interface to operate against.