Express.js Cannot read property 'req' of undefined
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...
When you passed res.send
as an argument to the getContents
function (panels.getContents(req.query.content, user.innId, res.send);
) it is passed in a new variable and you called it callback
.
So res.send
is now stripped from it's original/supposed-to-be lexical context
/binding
which is the res
object in this case, and it appears that res.send
uses the req
property in the res
object using this.req
.
What you've done here is unintentionally changing the value of this
for the function send
to be the global object.
Solution
there are two ways to fix this problem
panels.getContents(req.query.content, user.innId, () => res.send);
// OR
panels.getContents(req.query.content, user.innId, res.send.bind(res));
in the second solution, you bind the function res.send
to always treat any value of this
to be the res
object (bind it to the res object
)
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)
}
}