Cannot read properties of undefined (reading 'fitness') - javascript

We are currently making a website for our roommate matching project. We are at the point where we are making the results page, which should display the 3 most compatible roommates based on some questions where the algorithm calculates their compatibility percentage.
We are now trying to pass the object called "fitness" that holds all of the results, which would be:
let fit = {
fitness: Math.round(sum * 100),
email: resident[i].email,
name: resident[i].firstName + " " + resident[i].lastName,
};
To our .ejs file called results.ejs. The fit object gets pushed into our global "fitness" variable.
This is our post request:
router.post("/results", function (request, response) {
let item = request.body;
let applicant_name = request.body.firstName;
client.connect(function (err) {
const db = client.db("myFirstDatabase");
assert.equal(null, err);
db.collection("residents").insertOne(item, function (err, result) {
assert.equal(null, err);
console.log("Item inserted");
});
});
client.connect(function (err) {
const db = client.db("myFirstDatabase");
assert.equal(null, err);
db.collection("residents")
.find({})
.toArray(function (err, resident) {
if (err) throw err;
/* weighting(request); */
compatability(request, resident);
});
});
client.close();
response.render("pages/results", {
applicant_name: applicant_name,
resident: fitness,
});
});
In our results.ejs file, we use Bootstrap cards in order to display the 3 most compatible roommates. This is the code we use in the results.ejs file:
<p class="card-text">
Compatibility %: <%= resident[0].fitness %><br />
Name: <%= resident[0].name %> <br />
Email: <%= resident[0].email %><br />
Location: <br />
</p>
Whenever we run the post request, we get an error saying "Cannot read properties of undefined (reading 'fitness')", but if we refresh again, the Node.js server crashes, but it displays the actual values we want to get. If we add a "?" after "resident[0]", it runs the page without any errors but does not display any information from the object.
We have tried numerous things but cannot find the right solution to solve this issue.
Thanks in advance.
EDIT
The whole code from the .ejs file and our serverside code, with photos of how it looks on the website: https://pastebin.com/zRxdk6aq

The error "Cannot read properties of undefined (reading 'fitness')" in this case means that resident[0] is undefined. This is probably due to the fact that residentis either undefined or is an empty array (length 0).
From the code you provided I can't see, where you actually push your object into the global fitness variable or where the variable is defined. Maybe that's were the problem is. To help you further with that, it would certainly help if you could post the code for that.
Edit:
Looking at the new code you provided in your pastebin, it looks like you're using asynchronus functions but are expecting a synchronus behaviour. Your current code looks essentially like this:
router.post("/results", function (request, response) {
client.connect(function (err) {
// Insert data into db
});
client.connect(function (err) {
// Fetch data from db
});
client.close();
response.render("pages/results", {
applicant_name: applicant_name,
resident: fitness,
});
});
The problem with that is, that your program does not wait for your DB-operations to finish before rendering your page. This is because of how callback functions work.
I presume you are using the mongoDB driver for nodejs. Besides classic callback options, it also allows for the use of async/await:
router.post("/results", async (request, response) {
//
await client.connect();
const db = client.db('myFirstDatabase');
await db.collection("residents").insertOne(request.body);
let residents = await db.collection("residents").find({}).toArray();
let fitness = compatability(request, resident);
client.close();
response.render("pages/results", {
applicant_name: request.body.firstName,
resident: fitness,
});
});
function compatability(request, resident) {
let fitness = [];
// Do your calculations here
return fitness;
}
This way your request handler waits until you inserted your new item into the db and fetched the existing ones. Only after that, it renders your page and thereby ensures, that the fitness variable is already filled with data.
Note that in this example I also suggest to change your compatability function so that it returns your `fitnessĀ“ variable instead of making it global.
If you aren't familiar with async/await I recommend that you also try to learn about Promises.

Related

Handling database objects in node js

In general how does javascript interpret a Database {} object? I am writing some back end scripts to handle a registration form verification. In particular I need to ensure that the username and email used to register is not currently in use. To do this I use the sqlite3 package and use a few db.get calls to determine if there are existing entries in my database for the username and email used on the registration form. I want to use the return of db.get to check if it is empty or not and use this conditional to perform the necessary task. However the db.get returns a Database {} object which I am unfamiliar of how to work with.
Hopefully the following pseudo describes the issue better. Here uname returns Database {} and so never fails the if statement.
function existance(username, email) {
let uname = db.get(sql, username, callback(err, throw));
if (uname) {
let errors = {username: 'Username already in use.'};
return errors;
}
};
EDIT
I have since used Gajiu's recommendation but still having issues. So I have two files:
registrant_existence.js
// necessary requirements and initialisation
function registrant_existence(username) {
let uname;
let sql = 'SELECT username FROM table WHERE username=?';
db.get(sql, username, function(err, row) {
if (err) {
throw err;
} else {
uname = row;
console.log(uname);
}
});
console.log(uname);
if (uname !== undefined) {
return {username: 'Username already in use.'};
} else {
return 'DNE';
}
};
module.exports = registrant_existence;
register.js
let registrant_existence = require("path to registrant_existence.js");
// necessary requirements and initialisation
router.post('/', function(req, res) {
let existence = registrant_existence(req.body.username, req.body.email);
if (existence != 'DNE') {
// render registration page notifying the user
// that the username is already in use
} else {
// register the new user details
}
});
The uname variable is undefined always. I placed the console.log(uname) in two spots in registrant_existence.js as is seen above to see what is happening. Two strange things occur.
The first is that the console.log(uname) outside the db.get() displays undefined in the console and the console.log(uname) inside the db.get() displays the expected string (a username I know is in my database).
The second is that the console.log(uname) outside the db.get() is displayed before the console.log(uname) _inside the db.get() in my console.
I have no idea why these things are happening. Does anyone have any suggestions?
You should try something like that:
db.get(sql, username, (err, data) => {
// process the data here
if (err) {
return console.error(err.message);
}
return data
? console.log(data.id, data.userName)
: console.log('No username found');
});
I guess you are looking for a wrapper around your Database object, something like an Object Relational Mapper (ORM), this one is used regularly https://sequelize.org/master/manual/getting-started.html
On the other hand for your specific use case you might want to get a look at this https://www.sqlite.org/lang_createtable.html#unique_constraints: unicity is usually via constraints in the data store.

NodeJS - Need help understanding and converting a synchronous dependant MySQL query code into something usable

this is my second Node project. I am using Node, Express, MySQL.
What I am doing is, I have an array of names of users that have posted something, I then loop over those names and for each of them I do a connection.query to get their posts and I store those into an array(after that I do some other data manipulation to it, but that's not the important part)
The problem is: my code tries to do that data manipulation before it even receives the data from the connection.query!
I google-d around and it seems async await is the thing I need, problem is, I couldn't fit it in my code properly.
// namesOfPeopleImFollowing is the array with the names
namesOfPeopleImFollowing.forEach(function(ele){
connection.query(`SELECT * FROM user${ele}posts`, function(error,resultOfThis){
if(error){
console.log("Error found" + error)
} else {
allPostsWithUsername.push([{username:ele},resultOfThis])
}
})
})
console.log(JSON.stringify(allPostsWithUsername)) // This is EMPTY, it mustn't be empty.
So, how do I convert that into something which will work properly?
(Incase you need the entire code, here it is: https://pastebin.com/dDEJbPfP though I forgot to uncomment the code)
Thank you for your time.
There are many ways to solve this. A simple one would be to wrap your function inside a promise and resolve when the callback is complete.
const allPromises = [];
namesOfPeopleImFollowing.forEach((ele) => {
const myPromise = new Promise((resolve, reject) => {
connection.query(`SELECT * FROM user${ele}posts`, (error, resultOfThis) => {
if (error) {
reject(error);
console.log(`Error found${error}`);
} else {
resolve({ username: ele });
}
});
});
allPromises.push(myPromise);
});
Promise.all(allPromises).then((result) => {
// your code here
})
You can read more about promise.all here

postgres with nodejs - how to return query result

I've had this problem before twice. The first time, it was because the returned object was actually an unfulfilled promise, and I was able to get around it by throwing all my code inside of the block so that it would execute in there. Not exactly an ideal solution but it worked and I was in a hurry.
The second time I was using pg-promise instead of just pg, and when this issue popped up I thought "it must be that damn promise again", so instead of tearing my hair out I just uninstalled pg-promise and went with the regular postgres package for nodejs instead. Well, the problem is still here and I'm tearing my hair out.
I'm making an API. I have a controller with functions such as getPayment and postPayment and so on. At some point in these functions, I need to get information from or into the database. In another file, which I called postgres.js, I have a connection to the database and then functions which I can call to insert data, select, etc.
Here is postgres.js as it is right now (this works but only for printing to console):
module.exports = {
select: select
};
const { Client } = require('pg');
const client = new Client({
user: 'myUser',
host: 'localhost',
database: 'myDB',
password: 'myPassword',
port: 5432,
});
client.connect();
function select(){
client.query('SELECT * FROM payments', (err, res) => {
console.log(err ? err.stack : res.rows[0].id);
client.end();
});
}
As you can see right now all it does is get the payments from the table and print a value (in this case, the first row's id) to console.
Here is the code that calls this function, from the controller (payments.js):
var pg = require('../../config/postgres.js')
...
function getPaymentInfo(params){
// get info from the DB
// disregard the fact that I'm not sending any params right now, this is for testing
var result = pg.select();
console.log(result);
console.log(typeof result);
}
Calling pg.select() does in fact activate the select function on postgres.js (and the console log correctly prints the data in res.rows[0].id) however when I try putting a return res.rows[0].id there in order to actually return the information that was retrieved from the database, what comes back into the var result is "undefined" according to the console. Here are a few examples of things I've tried to do to get the data out:
function select(){
var i = '';
client.query('SELECT * FROM payments', (err, res) => {
//console.log(err ? err.stack : res.rows[0].id);
i = err? err.stack : res.rows[0].id;
client.end();
});
return i;
}
I tried this with and without commenting the console.log line, either way it doesn't work.
function select(){
client.query('SELECT * FROM payments', (err, res) => {
//console.log(err ? err.stack : res.rows[0].id);
return res.rows[0].id;
//client.end();
});
}
I figure this would be wrong since I'm returning without checking for an error, but I tried just to see what would happen... undefined again.
function select(){
client.query('SELECT * FROM payments', (err, res) => {
//console.log(err ? err.stack : res.rows[0].id);
return err ? err.stack : res.rows[0].id;
//client.end();
});
}
This time checking for err, still returns undefined.
function select(){
return client.query('SELECT * FROM payments', (err, res) => {
//console.log(err ? err.stack : res.rows[0].id); // Hello World!
client.end();
return err ? err.stack : res.rows[0].id;
});
}
Returning the return from client.query(etc etc);.. same thing as before. Also tried without the explicit return, without checking for error, all the same.
Here's the last test I tried:
function select(){
var i = 'original i';
client.query('SELECT * FROM payments', (err, res) => {
console.log(err ? err.stack : res.rows[0].id); // Hello World!
if (err){
i = "i has changed!";
} else {
i = "i has not changed!";
}
client.end();
});
return i;
}
The console shows this:
original i
string
2
That last "2" being res.rows[0].id.
I really believe this must have something to do with the blasted "promises" and asyncronous stuff. But my previous "solution" won't fit here. I can't just do everything inside of that block where I have access to res. I have to be able to return some data back to getPaymentInfo().
It seems that client.query(...) is waiting for every line in getPaymentInfo() to be ran before actually doing anything. I can see that because in the console, the results of console.log(result) and console.log(typeof result) appear before the line that shows information from the table (from console.log(err ? err.stack : res.rows[0].id) ). That is absolutely infuriating.
I called the pg.select() function. That function calls client.query(). client.query should do its thing, return to select(), which would then return to getPaymentInfo() and then keep going and do what the next lines say. That's normal behaviour for any program. Clearly that's not happening here.
I saw some examples out there that used await, but when I tried to add that copying the examples, it gave me a syntax error as if it didn't recognize what await is supposed to be. There was an example with yield as well, same error. It doesn't know what those commands mean. I'm using the latest version of nodejs (v8.10.0).
I also tried using then to get it to wait:
function generatePaymentInfo(params){
...
pg.select().then(function(v) {
console.log(v);
});
...
}
It gives me this error:
TypeError: Cannot read property 'then' of undefined
What's going on here? How do I get it to do things in the right order? All I want is to get some information from a table. Every example for the pg package (and pg-promise as well) I see out there just has a console.log() representing "success". In reality what anyone would want is to have actual access to the data.

nodejs recursively fetch all pages from different REST end points

I am kind of struggling with my JavaScript. Still getting my head around the callbacks. I have a function which recursively fetches the next link page from an REST endpoint.which works fine for a single url
Now i need to use the same recrusive function to do same for other end points. thats where iam struggling now. It is obvious that iam missing a point or i badly understood callback concept.
please excuse the formatting What i have done so far ..
function getFullLoad(rest_url,callback){
mongoose.connect(url, function(err) {
console.log(url);
if (err) throw err;
console.log("successfully connected");
getToken(function(err, token) {
if (err) throw console.error(err);
console.log('using access token to retrieve data :', token[0]
['access_token']);
var options = {
'auth': {
'bearer': token[0]['access_token']
}
}
request.get(rest_url, options, function(error, response, body) {
if (!error && response.statusCode === 200) {
var data = JSON.parse(body);
//save data in mongo
});
}
var next_page = JSON.parse(body)["_links"]["next"]
if (next_page) {
var next_page_url = JSON.parse(body)["_links"]["next"]["href"];
console.log("Fetching data from ", next_page);
//recrusively call back with next url
getFullLoad(next_page_url,callback);
} else {
callback();
} else {
if (error) throw error;
}
});
rest_url= "http://myrest_url"
//this bit is working if there is a single url
getFullLoad(rest_url, function() {
console.log('done , got all data');
});
this works great ,now i need to call the same function with different urls . It may be some urls may not have not pagination.
so far this is my effort , it only takes the first url and then does nothing.
This bit is not working . what iam doing wrong? i tried creating another callback function. i guess my knowledge is quite limited on callbacks. any help very much appreciated
api_endpoints =[ {"url1": "http://url1"},{"url2": "http://url2"}]
api_endpoints.forEach(function(Item, endpoint) {
var endpoint_name = Object.keys(api_endpoints[endpoint]).toString());
var rest_url = api_config.BASE_URL + api_endpoints[endpoint]
[endpoint_name];
getFullLoad(rest_url, function) {
console.log('done');
});
});
Thank you for looking my question
With a bit of refactoring I think you can get something to work.
I would change your getFullLoad method to return a promise (the request.get) then in your foreach loop I would build up an array of promises that call the getFullLoad with the different urls.
Then use promises.all(arrayOfPromises) to execute them and manage the results.
It will be easier to understand and maintain long term.
Check out the promise documentation here:
Promise.all

Node.js & Node-Postgres: Putting Queries into Models

I would like to 'functionalize' my queries by putting them into functions which have apt names for the task.
I want to avoid putting everything in the req, res functions (my controllers), and instead put them in 'models' of sorts, that is, another JavaScript file that will be imported and used to run the functions that execute queries and return the results on behalf of the controller.
Assuming that I have the following setup for the queries:
UserController.js
exports.userAccount = function(req, res, next) {
var queryText = "\
SELECT *\
FROM users\
WHERE id = $1\
";
var queryValues = [168];
pg.connect(secrets.DATABASE_URL, function(err, client, done) {
client.query(queryText, queryValues, function(err, result) {
res.render('pathToSome/page', {
queryResult: result.rows
});
});
});
}
Here, while I'm in the query, I essentially redirect and render a page with the data. That works fine. But I want to take out all that pg.connect and client.query code and move it to a separate file to be imported as a model. I've come up with the following:
UserModel.js
exports.findUser = function(id) {
// The user to be returned from the query
// Local scope to 'findUser' function?
var user = {};
var queryText = "\
SELECT *\
FROM users\
WHERE id = $1\
";
var queryValues = [id];
pg.connect(secrets.DATABASE_URL, function(err, client, done) {
client.query(queryText, queryValues, function(err, result) {
// There is only ever 1 row returned, so get the first one in the array
// Apparently this is local scope to 'client.query'?
// I want this to overwrite the user variable declared at the top of the function
user = result.rows;
// Console output correct; I have my one user
console.log("User data: " + JSON.stringify(user));
});
});
// I expect this to be correct. User is empty, because it was not really
// assigned in the user = result.rows call above.
console.log("User outside of 'pg.connect': " + JSON.stringify(user));
// I would like to return the user here, but it's empty!
return user;
};
and I'm calling my model function as so:
var user = UserModel.findUser(req.user.id);
The query executes perfectly fine in this fashion - except that the user object is not being assigned correctly (I'm assuming a scope issue), and I can't figure it out.
The goal is to be able to call a function (like the one above) from the controller, have the model execute the query and return the result to the controller.
Am I missing something blatantly obvious here?
pgconnect is an asynchronous call. Instead of waiting for data to return from the database before proceeding with the next line, it goes ahead with the rest of the program before Postgres answers. So in the code above, findUser returns a variable that has not yet been populated.
In order to make it work correctly, you have to add a callback to the findUser function. (I told you wrong in a previous edit: The done parameter in pg.connect is called in order to release the connection back to the connection pool.) The final result should look something like this:
exports.findUser = function(id, callback) {
var user = {};
var queryText = "SELECT FROM users WHERE id = $1";
var queryValues = [id];
pg.connect(secrets.DATABASE_URL, function(err, client, done) {
client.query(queryText, queryValues, function(err, result) {
user = result.rows;
done(); // Releases the connection back to the connection pool
callback(err, user);
});
});
return user;
};
And you'd use it, not like this:
var user = myModule.findUser(id);
But like this:
myModule.findUser(id, function(err, user){
// do something with the user.
});
If you have several steps to perform, each of them dependent on data from a previous asynchronous call, you'll wind up with confusing, Inception-style nested callbacks. Several asynchronous libraries exist to help you with making such code more readable, but the most popular is npm's async module.

Categories

Resources