Nodejs, express routes as es6 classes

Multi tool use
Nodejs, express routes as es6 classes
I want to clean up my project a bit and now i try to use es6 classes for my routes. My problem is that this is always undefined.
var express = require('express');
var app = express();
class Routes {
constructor(){
this.foo = 10
}
Root(req, res, next){
res.json({foo: this.foo}); // TypeError: Cannot read property 'foo' of undefined
}
}
var routes = new Routes();
app.get('/', routes.Root);
app.listen(8080);
4 Answers
4
try to use the code to pin this
:
this
app.get('/', routes.Root.bind(routes));
You can get out of the boilerplate using underscore bindAll function. For example:
var _ = require('underscore');
// ..
var routes = new Routes();
_.bindAll(routes)
app.get('/', routes.Root);
I also found that es7 allows you to write the code in a more elegant way:
class Routes {
constructor(){
this.foo = 10
}
Root = (req, res, next) => {
res.json({foo: this.foo});
}
}
var routes = new Routes();
app.get('/', routes.Root);
This is happening because you've passed a method as a standalone function to express. Express doesn't know anything about the class that it comes from, therefore it doesn't know which value to use as this
when your method is called.
this
You can force the value of this
with bind
.
this
bind
app.get('/', routes.Root.bind(routes));
Or you can use an alternative construct for managing routes. You can still make use of a lot of the syntactic benefits for object oriented programming without classes.
function Routes() {
const foo = 10;
return {
Root(req, res, next) {
res.json({ foo });
}
};
}
const routes = Routes();
app.get('/', routes.Root);
app.listen(8080);
this
new
bind
There's a good list of resources here, on why ES6 classes are not as good as they might seem.
The above answers seem a bit over complicated. Checkout what I've done here:
class Routes {
constructor(req, res, next) {
this.req = req;
this.res = res;
this.next = next;
this.foo = "BAR"
// Add more data to this. here if you like
}
findAll (){
const {data, res,} = this; // Or just reference the objects directly with 'this'
// Call functions, do whaterver here...
// Once you have the right data you can use the res obejct to pass it back down
res.json ({foo: this.foo}); // Grabs the foo value from the constructor
}
}
Now when it comes to using this class you can do something along the lines of this:
var express = require('express');
var router = express.Router();
var {Routes} = require('./Routes');
router.get('/foo', (req, res, next) => {
new Routes(req, res, next).findAll();
});
I would seperate the two files, so that you just require the Routes
class into your Router
file.
Routes
Router
Hope this helped!
This seems a lot more complicated than the accepted answer. This requires you to have private members of your class for req, res, and next(). These class members really are just middleware functions that need only to provide the same signature as the desired Express middleware type. Using the
.bind
method is the best solution to this design consideration. As a note, I'm doing this in TypeScript & seems to be working out great.– Michael Leanos
Nov 22 '16 at 1:12
.bind
Or if you don't like binding the context per routes, you can optionally bind it to methods in your class' constructor itself.
E.g:
constructor() {
this.foo = 10;
this.Root = this.Root.bind(this);
}
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
until es7 gets here - I love _.bindAll I was completely unaware of it. It is much nicer than binding users to itself in every route!
– Huston Hedinger
Jul 22 '16 at 23:40