15

I want my logger middleware to log each matched route when response is sent. But there may be any number of nested subroutes. Let's suppose I have this:

var app = express();
var router = express.Router();

app.use(function myLogger(req, res, next)
{
    res.send = function()
    {
        //Here I want to get matched route like this: '/router/smth/:id'
        //How can I do this?
    });
}

app.use('/router', router);

router.get('/smth/:id', function(req, res, next)
{
  res.send(response);
});

Is it possible?

Andrey Kon
  • 747
  • 2
  • 6
  • 16

2 Answers2

18

Because app-level middleware has no knowledge of routes, this is impossible. However, if you use your logger middleware as route middleware like:

router.get('/smith/:id', logger, function (req, res) { ... });

You can use a combination of two parameters on the request object:

req.route.path => '/smth/:id'
req.originalUrl => '/router/smth/123'

I'll leave it up to you how you want to combine both into one string.

rouan
  • 5,339
  • 6
  • 22
  • 36
srquinn
  • 10,134
  • 2
  • 48
  • 54
  • 3
    Looks like that `req.route` is not supported anymore. How can I do that in the recent versions of `express`? – Nawaz Jul 24 '19 at 09:02
  • @Nawaz `req.route` is supported in [5.x (most recent)](https://expressjs.com/en/5x/api.html#req.route), [4.x](https://expressjs.com/en/5x/api.html#req.route) and [3.x](https://expressjs.com/en/5x/api.html#req.route) – srquinn Jul 25 '19 at 22:22
  • 2
    ah, yes. I'm new to express, so I was trying to `console.log(req.route)` at the app-level middleware, where it is printing `undefined`. However, when I did the same in the router-level middleware, it prints what your links show. Thanks a lot. – Nawaz Jul 26 '19 at 05:46
4

Here's the code (in express 2.x)

// npm -v express       
// 2.14.2
var app = express();
var router = express.Router();

app.use(function(req, res, next) {
  var routes = app.routes; // See Reference 1.

  for(var method in routes) {
    if(routes.hasOwnProperty(method)) {
      for(var route in routes[method]) {
        if(req.url.toString().match(routes[method][route].regexp)) {
          console.log('Route Debugger: ' + routes[method][route].path);
        }
      }
    }
  }
  next();
});

app.use('/router', router);

router.get('/smth/:id', function(req, res, next)
{
  res.send(response);
});

What does this do?

  1. It queries the app.routes object. Now we have access to all the routes defined in our application.
  2. We match the current url req.url with the regular expression of each route.
  3. Since this is a application level middleware, it runs for every request. So you get logging like Route Debugger: /router/smth/:id, if you hit a url like /router/smith/123

Reference 1 : http://thejackalofjavascript.com/list-all-rest-endpoints/
Reference 2 : How to get all registered routes in Express?

Community
  • 1
  • 1
Adze
  • 155
  • 1
  • 1
  • 12
  • You should also add that this performs an O(n^2) blocking lookup on each request and duplicates what Express is doing under the covers for route matching. Not bad for a small order of routes, but for larger applications this is a huge bottleneck as compared to O(1) lookup at the route level. – srquinn Apr 01 '21 at 18:44