delvedor / find-my-way

A crazy fast HTTP router
MIT License
1.48k stars 133 forks source link

Adding and using names for the router #124

Open mahovich opened 5 years ago

mahovich commented 5 years ago

I propose to make the coolest opportunity - to set a name for the router, similar to how it was done for koa-router

The name can be set as follows:

router.on('GET', '/example', { name: 'example' }, (req, res, params) => {
  // your code
})

// OR

router.on('GET', '/employee/:userId', { name: 'user' }, (req, res, params) => {
  // your code
})

for use:

router.url('example');
// => "/example"

router.url('user', { userId: 2 });
// => "/employee/2"

There are many options for using URL generation, for example, it can be used to generate sitemap.xml, for redirect, and the coolest thing is to use it in html templates.

Why is this cool? After changing the URL in the code, there is no need to additionally change anything at all! All links placed and used by the code using router.url will automatically change!

Now after changing the URL, you need to make more changes, for example, to make a search and replace in code, scan the site for broken links. But after changing the URL, you can forget to make additional changes at all =)

mcollina commented 5 years ago

This is actually a very interesting feature, I used something similar in ruby for a long time, and I think it might be worthwhile adding. However, I do not want to make the name unique on the system.

Specifically:

router.on('GET', '/example', { name: 'example' }, (req, res, params) => {
  // your code
})

// OR

router.on('GET', '/employee/:userId', { name: 'example' }, (req, res, params) => {
  // your code
})

Should not throw, and the system should enable disambiguation.

How do you think we should handle that case?

mahovich commented 5 years ago

This is actually a very interesting feature, I used something similar in ruby for a long time, and I think it might be worthwhile adding. However, I do not want to make the name unique on the system.

Specifically:

router.on('GET', '/example', { name: 'example' }, (req, res, params) => {
  // your code
})

// OR

router.on('GET', '/employee/:userId', { name: 'example' }, (req, res, params) => {
  // your code
})

Should not throw, and the system should enable disambiguation.

How do you think we should handle that case?

IMHO, the name must be unique (as well as the URL). When copying and pasting a router with an URL, you can forget to change the name and this will be fatal for the project, because many links will become incorrect. In addition, when creating a new rule, there will be a need to independently verify the uniqueness of the name...

mcollina commented 5 years ago

I would prefer to make them non-unique. In Fastify we use an encapsulation pattern, so a full plugin could be mounted with a specific prefix. Unfortunately having a single namespace significantly limits the use of this feature for us.

mahovich commented 5 years ago

The decision on the uniqueness or non-uniqueness of the name may be at the discretion of the author of the repository. In order for this functionality to be in demand for a larger audience (including for a larger number of frameworks), can consider a new option, for example:

const router = require('find-my-way')({
  uniqueName: true
})

// OR notUniqueName

*but this is not necessary - it is more important not to complicate the plugin.

In any case, I will be very happy with any implementation of this functionality, since This is a really necessary opportunity. Earlier I used similar in Python. And I was extremely surprised that most of the javascript routers do not have this.

jean-michelet commented 6 months ago

I would prefer to make them non-unique. In Fastify we use an encapsulation pattern, so a full plugin could be mounted with a specific prefix. Unfortunately having a single namespace significantly limits the use of this feature for us.

I think the router should offer route collection management and allow paths and names to be prefixed by collection. I'm not making this up, it's actually how the Symfony routing component works: https://github.com/symfony/routing/blob/7.0/RouteCollection.php

Collections could be merged with other collections, at which point we check for name uniqueness. Then, Fastify could be updated to handle these collections via its plugin system, right?

// Is not a named route, so it's impossible to generate an url from it
fmw.on('GET', '/contact', () => {})

const blogRoutes = fmw.createCollection('/blog', 'blog_')
blogRoutes.get('/new', 'new', (req, res, params) => {})
blogRoutes.get('/:id', {}, 'show', (req, res, params) => {})

const profileRoutes = fmw.createCollection('/profile', 'profile_')
profileRoutes.get('/', '', (req, res, params) => {})
profileRoutes.get('/settings/', 'settings', (req, res, params) => {})

// We check that no route with existing name is merged
fmw.mergeCollection(blogRoutes)
fmw.mergeCollection(profileRoutes)

fmw.url('blog_new', {}, { kind: 'relative_path' }) // /blog/new
fmw.url('blog_show', { id: 1 }, { kind: 'relative_path' }) // /blog/1
fmw.url('profile_', {}, { kind: 'relative_path' }) // /profile
fmw.url('profile_settings', {}, { kind: 'relative_path' }) // /profile/settings

Anyway, I think it should be a separate PR from the URL generation, which is a tricky subject in itself...

jean-michelet commented 6 months ago

Is this feature still considered?

mcollina commented 6 months ago

I think so, yes.

jean-michelet commented 6 months ago

I'm willing to work on it, but I'd like to know which strategy would suit you best. If my idea of creating collections doesn't fit, I may try* suggest other solutions.

mcollina commented 6 months ago

let us know! even a draft pr is fine.