My Express.js app has been throwing Cannot read property 'req' of undefined
. In essence, it listens for a GET
request, grab the content
query, and then reply with a table. Here's the parts that present the problem.
index.js
var panels = require('./modules/panels.js');
app.get('/panel', function (req, res) {
var user;
if (user = req.session.user) {
panels.getContents(req.query.content, user.innId, res.send);
} else {
res.sendStatus(401);
}
});
modules/panels.js
exports.getContents = function(panelName, innId, callback) {
var response = "";
switch (panelName) {
case 'tenants':
con.query(queryString, queryParams, function(err, rows) {
if (err) {
handle(err);
} else {
if (rows.length == 0) {
var tenants = 0;
var debtors = 0;
} else {
var tenants = rows[0].tenants;
var debtors = rows[0].length;
}
response = convertToHTMLTable(rows);
}
callback(response); /*THE ERROR POINTS HERE */
});
break;
/* other cases here */
default:
response += "Invalid Request";
callback(response);
}
}
What did I do wrong? My guess is that I'm not suppose to pass res.send
as a callback. So, how can I fix it?
I also experienced this error, and after chewing on the error message for a bit and staring at my code for too long, It clicked.
I (and the asker above) had code that looked like this:
somethingAsync
.then(res.send) // <-- storing send() divorced from parent object "res"
}
The problem is that when res.send gets called later, it is divorced from its dynamic scope--which means that calls to this
inside of the send
method, which would normally refer to res
, now refer to global
. Evidently, res.send
contains a reference to this.req
, given the error message.
An equivalent scenario:
res = {
send: function() {
console.log(this.originalMessage.req);
},
originalMessage: { req: "hello SO" },
};
res.send();
// # "hello SO"
const x = {};
x.send = res.send;
x.send();
// # Uncaught TypeError: Cannot read property 'req' of undefined
Or, to put it another way:
new Promise(_ => _())
.then(_ => console.log(this.msg.req));
// # Uncaught (in promise) TypeError: Cannot read property 'req' of undefined
Two solutions:
handler(req, res) {
somethingAsync
.then(() => res.send())
// ^ send will be called as a method of res, preserving dynamic scope
// that is, we're storing a function that, when called, calls
// "res.send()", instead of storing the "send" method of "res" by itself.
}
And a theoretically more idiomatic approach is
handler(req, res) {
somethingAsync
.then(res.send.bind(res))
//^ res is explicitly bound to the saved function as its dynamic scope
}
Pernicious little gotcha of dynamic scoping, that one. Seems like express should ditch the dynamic scoping there, or at least throw a better error...
try this in index.js
panels.getContents(req.query.content, user.innId, res);
and into panels.js
exports.getContents = function(panelName, innId, response) {
var response = "";
switch (panelName) {
case 'tenants':
con.query(queryString, queryParams, function(err, rows) {
if (err) {
handle(err);
} else {
if (rows.length == 0) {
var tenants = 0;
var debtors = 0;
} else {
var tenants = rows[0].tenants;
var debtors = rows[0].length;
}
response.send(convertToHTMLTable(rows));
}
});
break;
/* other cases here */
default:
error += "Invalid Request";
response.send(error)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With