When you do this:
router.get('/users', userController.list);
what is passed to your router is just a reference to the .list method. The userController instance is lost. This is not unique to routers - it is a common property of how things are passed in Javascript. To further understand what you basically do is:
let list = userController.list;
And, in Javascript strict mode, when you call a regular function without reference to an object, for example the list() call above, then this will be undefined inside the function. This is what happens in your example. To fix this, you must ensure that your method is called with the proper object reference, as in userController.list(...) , so that the interpreter sets this accordingly.
There are several ways to solve this problem:
Create your own wrapper of functions
router.get('/users', function(req, res) { userController.list(req, res); });
This works in any version of Javascript.
Using .bind() to make a wrapper for you that calls it with the correct object
router.get('/users', userController.list.bind(userController));
This works in ES5 + or with .bind() polyfill.
Use the ES6 arrow label
router.get('/users', (...args) => userController.list(...args));
It works in ES6 +
Personally, I prefer the .bind() implementation, because I think it is simply simpler and more declarative / understandable than any other, and the ES6 "shortcut" is not much shorter.