1

I have a set of routes that are defined in a routes.js file like so:

const express = require('express');
const router = express.Router();
const controller = require('../controllers/mycontroller');

router.get('/', controller.home);
router.get('/about', controller.about);
...

What I want to do is to be able to add some meta-data to each of these definitions, like so:

router.get('/', controller.home).metadata({title: 'Home Page', internal: true});

And then be able to read them in a for loop:

router.stack.forEach((route) => {

    const metadata = ???
});

How can I achieve this? I would need this information inside the controller as well, so in controller.home I will need to pass title to the appropriate template.


Can I use bind() for this? I thought maybe I can do:

router.get('/', controller.home).bind(key: 'value');

so that in the controller, I can do this.key. But how can I access this key in a router.stack for loop?

darksky
  • 1,955
  • 16
  • 28
  • What do you want to use the meta data for? If it is some kind of logging of access to the different controller functions, then it might be easier to just create a middleware, which logs it, when the path is accessed. – Geshode Mar 31 '23 at 00:53
  • It is used for indexing pages and later searching them. The reason I don't want to use middlewares is that I don't want this information to affect each and every request. This is static information that's mostly accessed once when the app starts to build the set of pages that need to be crawled, along with their titles and other meta-data. I can obviously make a second file with a big list of all the pages, but then I'd have to edit that independently of the router definitions. It would be much more useful to have them defined in the same place. – darksky Mar 31 '23 at 00:59
  • Wouldn't you access it with `route.route.key` inside the forEach? Since `router.stack` contains `layer` objects, which contain the `route` object and you bind the metadata to the route. – Geshode Mar 31 '23 at 01:20
  • That doesn't work. I get `undefined`. I got the idea of using `bind()` from [this question](https://stackoverflow.com/questions/18014177/adding-metadata-to-express-routes) but I now realize I didn't even properly write it. The bind function was called on the controller like `controller.home.bind` not on the call to get. – darksky Mar 31 '23 at 01:28
  • You should be able to access the function through `route.handle` in the forEach, but I am not sure, if you can bind meta data to it. – Geshode Mar 31 '23 at 01:33
  • Yes, `route.handle` returns the function that handles the request. But how can I access the `this` context of that function outside of it? I don't know if that's possible, so using `route.handle` is probably not useful. – darksky Mar 31 '23 at 01:57

1 Answers1

0

Not sure if this is the best approach, but I ended up defining a "route generating" function that takes in a url, title, metadata, and a list of middlewares and defines an appropriate get route. This acts as a small wrapper around the router module.

in utils/router.js:

const express = require('express');
const router = express.Router();

const addTitle = (title) => (req, res, next) => {
    res.locals.title = title;
    next();
}

const get = router.get;
router.get = function(url, title, internal, ...middlewares) {
    // do what's needed with metadata here
    // for example add them to the list of pages to be crawled
    get.call(router, url, addTitle(title), ...middlewares);
};

module.exports = router;

and in routes/my.routes.js:

const express = require('express');
const router = require('../utils/router');
const controller = require('../controllers/mycontroller');

router.get('/', 'Home', internal=true, controller.home);

...

module.exports = router;
darksky
  • 1,955
  • 16
  • 28