koa-wormhole
A simple, predictable, low-performance router for Koa similar to koa-router and Express 4's built-in router.
npm install --save koa-wormhole
Quickstart
const Router = ;const app = ;const router1 = ;router1;router1;const router2 = ;router2;router2;router2;app;app;app;
Router middleware (middleware mounted via router.use(...)
) are only run
if the request matches any of the router's routes. Else, the router is
skipped.
Usage
You can find a lot of examples in koa-wormhole's tests: https://github.com/danneu/koa-wormhole/blob/master/test/index.js
Basics
Just like a koa instance, a router instance has router.use(...middleware)
that takes one or more middleware generator functions.
const mw1 = {console;next;};const mw2 = {console;next;};router;app;
However, a router's top-level middleware will not run unless the request matches one of the router's routes.
Let's define a route for the above example.
router;router;app;
Since the two middleware were mounted before the route, they will run before the route:
$ curl http://localhost:3000/test
// executing mw1
// executing mw2
// executing GET /foo
//=> 'hello world'
And, unlike in koa-router, mount order matters in koa-wormhole.
If we mount the route before the middleware, then the middleware will not
get to execute unless the route yield* next
.
This is just predictable middleware behavior.
router;router;app;
$ curl http://localhost:3000/test
// executing GET /foo
//=> 'hello world'
And here's an example of what that behavior looks like when we yield next from a route handler. The request will continue down the stack, possibly matching downstream handlers.
router;router;app;
$ curl http://localhost:3000/test
// executing handler1 and yielding next
// executing handler2 and responding
//=> 'ok'
You can also mount middleware to a specific route to be run before the handler:
router;
$ curl http://localhost:3000/test
// executing mw1
// executing mw2
// executing handler
//=> 'ok'
And it handles flattens out arrays. These are all the same:
router;router;router;router;
URL params
koa-wormhole uses path-to-regexp to turn route paths into regular expressions, so it has the same syntax as Express4's router and koa-router.
Read its docs for more examples.
koa-wormhole exposes URL params via the this.params
object.
router;
URL param middleware
You can DRY up repetitive logic with Router#param(key, middleware)
.
For example, it's useful for auto-loading resources.
router;// `this.resource` is now guaranteed to exist in these handlersrouter;router;router;router;// and the param middleware will not be called for theserouter;router;
Method chaining
Router#use
and all of the Router#{verb}
s return the router instance,
so you can chain them if you'd like.
router// <-- only applies to downstream routes;;app;
Nested Routers
3-layers deep:
const app = ;const r1 = r2 = r3 = ;r3;r2;r1;app;app;
curl http://localhost:3000
// hello, world!
General idea
I thought it'd be fun to implement a router with koa-compose, composing one long chain of generator middleware for each router.
Goals:
- Predictable behavior
- Simple implementation
koa-wormhole vs Express 4's built-in router
- Express 4's routing docs: http://expressjs.com/guide/routing.html
The key difference is that koa-wormhole only pipes a request through a mounted router if the request actually matches one of the router's routes.
Consider this Express example:
// ------------------------------------------------------------// router.js// ------------------------------------------------------------const router = ;router;router;moduleexports = router;// ------------------------------------------------------------// server.js// ------------------------------------------------------------const app = ;const router = ;app;app;app;
In Express, requests are always piped through routers, so top-level router middleware will always execute:
$ curl http://localhost:3000
// inside router middleware
//=> 'homepage'
$ curl http://localhost:3000/router
// inside router middleware
//=> 'inside router route'
Notice that the route's top-level middleware is always run.
Now let's look at the exact same example in koa-wormhole:
// ------------------------------------------------------------// router.js// ------------------------------------------------------------const router = ;router;router;moduleexports = router;// ------------------------------------------------------------// server.js// ------------------------------------------------------------const app = ;const router = ;app;app;app;
$ curl http://localhost:3000
//=> 'homepage'
$ curl http://localhost:3000/router
// inside router middleware
//=> 'inside router route'
koa-wormhole skips over the router when requesting the homepage since the router had no matching route.
Why?
I just find this behavior more useful, and it's what I'm used to with koa-router.
I initially considered solutions that let you distinguish between router middleware that always runs and router middleware that only runs on router match, but I couldn't think of a use-case for that behavior.
koa-wormhole vs koa-router
koa-router has some undefined behavior and unexpected idiosyncrasies.
One example is that top-level koa-router middleware are always run before the matched handler. Consider this koa-router example:
router;router;router;router;
$ curl http://localhost:3000
// executing router middleware 1
// executing router middleware 2
// executing router middleware 3
// executing router GET / handler
//=> 'ok'
I find this behavior unexpected and counter-intuitive. I'd expect the 'GET /' handler to execute first and only call the downstream middleware if it yields next, which is what koa-wormhole will do.
This may be a bug: https://github.com/alexmingoia/koa-router/issues/194
I tried to improve upon koa-router by following standard middleware intuition like declaration-order sensitivity.
License
MIT