koa-router: Unable to reuse nested routers

I need to mount a shared router at different paths in my application. Something like this:

sharedRouter.get('/hello', (ctx) => {
    ctx.body = 'Hello World!';
});

rootRouter.use(sharedRouter.routes());
rootRouter.use('/foo', sharedRouter.routes());
rootRouter.use('/bar', sharedRouter.routes());

app.use(rootRouter.routes());

Then, I expect the following routes to return the same thing:

  • GET /hello
  • GET /foo/hello
  • GET /bar/hello

However, with koa-router@7.0.1, all of them throw 404 errors. What is wrong with this?

Please, note that my real use case is a bit more complex: each mountpoint is supposed to receive its own middleware, which alters the behavior of the shared router.

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 24
  • Comments: 18

Commits related to this issue

Most upvoted comments

I’m also waiting on a resolution of this issue

@tiant167 Thanks, but with this solution is only for using sharedRouter in one file. It is good to be able to nest router in a different part of application. For example, you have ‘/foo’ router in one file and ‘/bar’ in another.

@nadavoosh your solution throwing an error. But I slightly changed it and now this works for me:

const _ = require('lodash'); rootRouter.use('/foo', _.cloneDeep(sharedRouter).routes()); rootRouter.use('/bar', _.cloneDeep(sharedRouter).routes());

Maybe it depends on version?

After read the document of path-to-regexp, we are able to use RegExp-like string path to solve this problem.

For the nested router in the first comment, we can write router path as:

rootRouter.use('/(foo|bar)?', sharedRouter.routes());
  • GET /hello
  • GET /foo/hello
  • GET /bar/hello

Thanks for the succinct example and for pointing this out. I was able to reproduce what you’re seeing.

It boils down to the fact that a router is mutated when it is mounted at a prefix. See this line

When .routes() is called on sharedRouter, the returned layers are mutated in-place and the /foo prefix is added to the existing /hello-bound router. Further, when the sharedRouter is mounted again, it is further prefixed. The resulting path would then be /bar/foo/hello and if you request this path from your example you will get a 200.

I started some work making the layer immutable, but it isn’t as easy as I’d like yet. I’ll have some test cases and a fix soon, but I’d recommend treating routers as mutable and unusable after they’re mounted at a prefix for now.

This affects all versions of koa-router.

Sorry for the inconvenience!

7.4.0 still not work.

I ran into this issue too, and I solved it by doing

const _ = require('lodash');
rootRouter.use('/foo', _.cloneDeep(sharedRouter.routes()));
rootRouter.use('/bar', _.cloneDeep(sharedRouter.routes()));

which seems to work as intended.

@jbielick Okay! Thanks for your response. I will do it these days.