router icon indicating copy to clipboard operation
router copied to clipboard

Add Router events

Open hacksparrow opened this issue 8 years ago • 28 comments

The following events were added:

  1. handlestart - emitted when the router starts to handle the router stack.
  2. layer - emitted when a layer is matched by the router. It returns the matched layer.
  3. handleend - emitted when the router has completed handling the router stack.

This feature should help developers handle requirements as described in https://github.com/expressjs/express/issues/2501, https://github.com/expressjs/express/pull/3100, and similar.

Example of usage with Express:

app.router.on('handlestart', function () {
  console.log('------- %d -------', Date.now())
})

app.router.on('layer', function (layer) {
  console.log(layer)
})

app.router.on('handleend', function () {
  console.log('------- %d -------', Date.now())
})

@dougwilson @danieljuhl @evanshortiss \o/

hacksparrow avatar Oct 24 '16 10:10 hacksparrow

@hacksparrow this is nice. I guess "layer" would be the event I'm looking for, but where can I associate that to the request itself here? For example, being able to do the below, would be nice, but needs req to be injected:

app.router.on('layer', function (layer, req) {
  req.matchedLayers = req.matchedLayers ? req.matchedLayers.concat([layer]) : [layer]
})

evanshortiss avatar Oct 25 '16 18:10 evanshortiss

The end goal being that I can have a middleware like so:

function routeLogger (req, res, next) {
  res.on('finish', function () {
    // write to analytics, or log route
    console.log(req.matchedLayers); // Maybe do some formatting here to get the actual paths joined
  });
}

evanshortiss avatar Oct 25 '16 18:10 evanshortiss

We will also need to discuss how the events are bubbled (or not). For example, would a user be required to attach an event listener on every single router (and of course, understand the list of all their routers) or should this use a form of event bubbling between routers, similar to the DOM?

dougwilson avatar Oct 25 '16 18:10 dougwilson

@evanshortiss great suggestion. "handlestart" and "handleend" would need the request context too.

@dougwilson excellent feedback. I will implement a bubbling version as well for further thoughts on the feature.

hacksparrow avatar Oct 26 '16 15:10 hacksparrow

@evanshortiss the event handlers have been updated to the following:

app.router.on('handlestart', function (req, router) {

})

app.router.on('layer', function (layer, req, router) {

})

app.router.on('handleend', function (req, router) {

})

hacksparrow avatar Oct 27 '16 17:10 hacksparrow

That looks solid to me. Should provide plenty of context for introspection. Nice work! 👍

evanshortiss avatar Oct 27 '16 17:10 evanshortiss

@hacksparrow why add the router to the event? Isn't that available within the event handler as the context (this)?

dougwilson avatar Oct 27 '16 18:10 dougwilson

@dougwilson err yes! I will remove it from the params. Thanks.

hacksparrow avatar Oct 27 '16 18:10 hacksparrow

So, just for the record, the signature now is:

app.router.on('handlestart', function (req) {

})

app.router.on('layer', function (layer, req) {

})

app.router.on('handleend', function (req) {

})

hacksparrow avatar Oct 27 '16 18:10 hacksparrow

Does anyone have any thoughts on the event names? I'm neutral on them :)

dougwilson avatar Nov 03 '16 00:11 dougwilson

Please add some docs for this to the README.

crandmck avatar Nov 03 '16 00:11 crandmck

@dougwilson @crandmck I have updated the PR according to your suggestions.

For the record, the signature of the layer event listener now is:

app.router.on('layer', function (req, layer) {

})

hacksparrow avatar Nov 10 '16 10:11 hacksparrow

Thanks, docs LGTM.

crandmck avatar Nov 11 '16 19:11 crandmck

Anything blocking this? I would love to replace what I have been doing in middleware with these new events.

wesleytodd avatar Nov 13 '16 19:11 wesleytodd

There are still issues I wrote above unaddressed, with the biggest that it is reaching into the internals of Node.js events which is unlikely to keep working in the future.

dougwilson avatar Nov 13 '16 20:11 dougwilson

Ahh, sorry @dougwilson I didn't open the "outdated diffs". I completely agree with the comments, and am anxiously awaiting this merge :) Thanks for the good work @hacksparrow!!

wesleytodd avatar Nov 13 '16 20:11 wesleytodd

Also, what ever happened with the whole event bubbling discussion? I feel like I've mentioned it before, and @hacksparrow said he would look into it, but never really anything seemed to happen there. So what is the thoughts on that? Does that make it easier to accomplish whatever the goals of this PR are or not?

dougwilson avatar Nov 13 '16 21:11 dougwilson

@dougwilson I haven't made significant progress on event bubbling yet. Will update once done. In the mean time, it'd be great to hear the community's opinion about a bubbling and non-bubbling version.

hacksparrow avatar Nov 17 '16 00:11 hacksparrow

@dougwilson @hacksparrow just to be clear, by bubbling you mean bubble to an upper "app" (express) instance? So for submounted apps, the event will bubble to the parent app and so on?

evanshortiss avatar Nov 17 '16 00:11 evanshortiss

@evanshortiss bubbling within the routers (independent or the one that comes with Express) hierarchy. We were thinking along lines of the browser DOM events.

hacksparrow avatar Nov 17 '16 00:11 hacksparrow

TBH I think you have to do bubbling for layer events, if you don't I think the behavior will be unexpected. That being said, multiple start and end events seem problematic, or atleast overly complex. I see two options on that:

  1. "Bubbled" events get prefixed, or a different name altogether
  2. handlestart/handleend events don't bubble, but layer events still do

wesleytodd avatar Nov 17 '16 00:11 wesleytodd

To address the cases that I would use this for right away, here are two I can think of:

  1. Unbind events on the front-end when the NEXT handlestart event happens
  2. Monitoring performance metrics for each layer

So for number one, having multiple start events for one "run" of the router stack, would mean I would have to track it myself. Possible, obviously, but it would be nicer if the design compensated for this and gave me a simple way.

For number two, each "layer", IMO should include all sub routers. Thus requiring bubbling.

wesleytodd avatar Nov 17 '16 01:11 wesleytodd

So I am circling back on this, and after re-thinking it I do not think anything other than the layer event should be included. Here are my thoughts:

  1. There are complexities this introduced, as discussed above in detail. None of which have simple resolutions.

    • Bubbling: the semantics of this are hard to get right for start and end, but pretty clear for layer
    • Handle End: Currently it is the response end, but this can already be handled with the on-finished package, and the router end has little to no value.
  2. The feature's we have all mentioned can be solved in other ways:

    • Original Route/Matched Route: #34 solves this in a much better way
    • Metrics: If you actually care to get accurate metrics you should be capturing the start time sooner than this, and as mentioned above, handleend is an alias for onFinished(res, cb).
    • Logging: Because #34 gets you all the info you need, you can then just log in a middleware at the end of your stack

That all being said, the one thing we cannot do with ease is track stats and metrics on individual layers. Which is why I propose that this change just to:

  • layerstart: Same as the current layer event is now
  • layerend: Added once next is called after matching a layer

So I created #56 to illustrate and keep the discussion going.

wesleytodd avatar Mar 25 '17 19:03 wesleytodd

Hi guys! so is this still happening? :)

fcolella avatar Mar 06 '19 20:03 fcolella

Hi guys! so is this still happening? :)

If it is, as I said in my last comment, I would hope we just go with the layer events. That being said, I am not sure even this is the best way forward, and I have an idea which has been bubbling about adding a decorator mechanism which would allow for much more flexibility and also superseded the need to add these events at all.

I have left open my other PR since it might be good, but I think this PR should be closed. If no one disagrees (@dougwilson or @hacksparrow) then I will close this in a week (reminder already set to revisit).

wesleytodd avatar May 31 '19 21:05 wesleytodd

Ok, no comments. But @dougwilson it looks like I am not an owner on this org so cannot close this. 🤷‍♂️

wesleytodd avatar Jun 07 '19 18:06 wesleytodd

@dougwilson It looks like just adding me to the tc group is not enough to be able to close issues. Seems like there must be some settings to make that work, but I am not sure.

wesleytodd avatar Jun 09 '19 03:06 wesleytodd

Sorry @wesleytodd about that. I will check it out and make sure your permissions are in order. You should have the ability do to this, so I will check why here shortly 👍

dougwilson avatar Jun 09 '19 03:06 dougwilson

Finally coming back around here. This is superseded by https://github.com/pillarjs/router/pull/56 IIRC. Closing this, and assessing if that could possibly land for router@2 and express@5.

wesleytodd avatar Mar 16 '24 18:03 wesleytodd