You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In order to get the full path of a named route in a nested router using the .url() method, we have to call it on the base router (same principle applies for .redirect() and .route()):
// fooRouter.jsconstRouter=require('@koa/router');constfoos={};letnextFooId=1;constfooRouter=newRouter();fooRouter.post('create','/foos',(ctx,next)=>{foos[nextFooId]={id: nextFooId,foo: 'foo'};ctx.status=201;// Here I want to get the 'read' route for this resource so I can// send it back to the client, e.g. in the `Location` header or bodyconsole.log(fooRouter.url('read',{id: nextFooId}));// '/foos/1' - incomplete path// Need to use the base router in order to get the full path:console.log(ctx.router.url('read',{id: nextFooId}));// '/api/v1/foos/1'console.log(fooRouter.route('read')===ctx.router.route('read'))// falsenextFooId++;});fooRouter.get('read','/foos/:id',(ctx,next)=>{ctx.body=foos[id];});module.exports=fooRouter;
This is a little bit of a pain to remember, but makes sense when you consider that a nested router can't know where it's mounted (at least, not until request time), especially since it could potentially be mounted multiple times in different places. I think a note about this should probably be made in the docs, especially considering that in some previous versions (I know in 7.4.0 at least), calling .url() did actually give the full path... as long as you only mounted each router only once, otherwise it was very broken. It's easy to not realise that the behaviour changed.
That's only a minor issue, the bigger problem is that since we have to use the base router, the names we give to any routes have to be unique across the whole routing tree, or else there's no guarantee we'll get the one we expect. For example if we re-use fooRouter for v2 of the API like so:
a POST request to http://localhost:3000/api/v2/foos will log the v1 path instead.
Additionally, if we created a barRouter with the same structure as fooRouter (maybe we're using an abstraction like a factory to create routers for resources), we could run into a similar collision where the path for a bar resource is returned instead of a foo, or vice versa.
Workaround:
Since the route identifiers need to be globally unique, on a whim I tried using symbols in place of strings, which turns out to already work as-is! All you have to do is make sure you're not sharing references to the same symbol. The only issues I found with this approach are:
If the route attempting to be referenced doesn't exist, an error will be thrown because of this line:
returnnewError(`No route found for name: ${name}`);
Symbols can't be implicitly converted to strings, so name would just need to be wrapped in String() or similar.
It's slightly more verbose
It feels weird that it's necessary
Do you think this approach should be supported/documented (since there's almost no work needed), or should we look for a way to solve the issue that's a little more ergonomic?
The text was updated successfully, but these errors were encountered:
node.js version: 16.13.2
@koa/router
version: 10.1.1koa
version: 2.13.4Explanation:
In order to get the full path of a named route in a nested router using the
.url()
method, we have to call it on the base router (same principle applies for.redirect()
and.route()
):This is a little bit of a pain to remember, but makes sense when you consider that a nested router can't know where it's mounted (at least, not until request time), especially since it could potentially be mounted multiple times in different places. I think a note about this should probably be made in the docs, especially considering that in some previous versions (I know in 7.4.0 at least), calling
.url()
did actually give the full path... as long as you only mounted each router only once, otherwise it was very broken. It's easy to not realise that the behaviour changed.That's only a minor issue, the bigger problem is that since we have to use the base router, the names we give to any routes have to be unique across the whole routing tree, or else there's no guarantee we'll get the one we expect. For example if we re-use
fooRouter
for v2 of the API like so:a POST request to
http://localhost:3000/api/v2/foos
will log thev1
path instead.Additionally, if we created a
barRouter
with the same structure asfooRouter
(maybe we're using an abstraction like a factory to create routers for resources), we could run into a similar collision where the path for abar
resource is returned instead of afoo
, or vice versa.Workaround:
Since the route identifiers need to be globally unique, on a whim I tried using symbols in place of strings, which turns out to already work as-is! All you have to do is make sure you're not sharing references to the same symbol. The only issues I found with this approach are:
router/lib/router.js
Line 656 in 90dd73c
Symbols can't be implicitly converted to strings, so
name
would just need to be wrapped inString()
or similar.Do you think this approach should be supported/documented (since there's almost no work needed), or should we look for a way to solve the issue that's a little more ergonomic?
The text was updated successfully, but these errors were encountered: