lukeed / trouter

:fish: A fast, small-but-mighty, familiar fish...errr, router*
MIT License
634 stars 23 forks source link

option to stops at first match #10

Closed neves closed 5 years ago

neves commented 5 years ago

It's probably by design, but what about a option to stop at the first match? The script below shows that /users/new matchs with: /users/new, /users/:id and /users/*

const Trouter = require('trouter')
const router = new Trouter()

// match only with /users
router.get('/users', () => {
  console.log('users')
})

router.get('/users/new', () => {
  console.log('new user')
})

router.get('/users/:id', () => {
  console.log('edit user')
})

router.get('/users/*', () => {
  console.log('users/*')
})

// match /users/* /users/new and /users/:id
let obj = router.find('GET', '/users/new')

console.log(obj)

obj.handlers.forEach(fn => {
  fn()
})

// OUTPUTS:
// { params: { id: 'new', wild: 'new' },
//   handlers: [ [Function], [Function], [Function] ] }
// new user
// edit user
// users/*

Can you remember what's the behaviour of others routers?

lukeed commented 5 years ago

Hey,

It is by design & don't think I'll change it otherwise. It's meant to return all matching results, in order, and then it's up to the consumer to iterate thru until some condition.

In Polka, this is done here, looping thru all handlers until res.finished – aka, the response was terminated.

Before 3.0, I thought about going with a generator approach, but the overhead of setting that up and the repetitive find() calls made it not worth it in the end.

Most routers return the exact match – not the 1st match. They'll operate by specificity & not by order declared. This is how Trouter was originally, but completely broken Express compatibility, which was needed for Polka. Some routers will "build" a results list as it traverses down the specificity tree.

In your example, matching by specificity would mean that ('GET', '/users/new') would always (and only) return the .get('/users/new') handler, regardless of the order that you defined it. This is also true of this:

router.get('/users', ...);
router.get('/users/:id', ...);
router.get('/users/*', ...);
router.get('/users/new', ...);

That can't be true of Express-style apps. I overlooked that distinction the 1st time around.

In any event, prioritizing depth/specificity is "the correct" way to do it (and why Trouter did it originally) in the context of a tree router, but it's completely incompatible with the Express-style of application composition.

Hope that helps explain the state of things a bit. Closing, but open to discuss further :)

paulocoghi commented 5 years ago

When reading all the discussion, initially I was also a little concerned about not returning only the most exact match.

But, after thinking a little, IMHO I believe that the current trouter's approach is better.

@neves, I know it's only an example, but using different HTTP verbs for different operations may be a more interesting option when creating an API.

To create a new user, instead of using GET:

router.get('/users/new', ...);

Wouldn't it be better to use POST on /users (without /new )?

router.post('/users', ...);

In this way, I believe that there is much less chances of conflict and, for example, routes like /users/* can be used together, to do other necessary stuffs when dealing with users.

My 2c.


@neves, como anda Maringá? Talvez eu tenha exagerado em entrar a fundo no seu exemplo, mas achei importante caso você estivesse mesmo pensando em seguir nessa linha.