Nodejs wait for query - node.js

I'm using Nodejs with MongoDB(mongoose along with express).
Since I don't trust the user data, I need to verify it from the database.
input data:
{
"id": "someid",
"nottrusteddata": [ {"id": "1"}, {"id" :"2"}]
}
In my function, I'm verifying the data:
router.post("/validate", (req, res,next) =>{
let validated_data = validate_data(req);
console.log(JSON.stringify(validated_data));
const mydata = new Mydata({
id: req.body.id,
lst : validated_data
});
console.log("mydata: " + JSON.stringify(mydata));
/* Some Usefull stuff is here */
res.status(200).json();
}
function validate_data(req){
let validated_data = []
for(let i = 0; i < req.body.nottrusteddata.length; i++)
{
Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
.exec()
.then(dbobject =>{
if(dbobject) // not undefined, it exists in the database
{
// Some logic with the object returned from the database
let tmp_object = {};
tmpobject.id = dbobject.id;
// Append it to the list, so that the upper function can use it
validated_data.push(tmp_object);
}
})
}
return validated_data;
}
The desired output should contain the correct information coming from the database, however, due to the async nature of the nodejs, validated_data returns null.
I have also tried using Promise. I couldn't succeed it.
const validate_data = function(req){
return new Promise(function(resolve,reject){
let validated_data = []
for(let i = 0; i < req.body.nottrusteddata.length; i++)
{
Databaseobject.findOne({'id': req.body.nottrusteddata[i].id})
.exec()
.then(dbobject =>{
if(dbobject) // not undefined, it exists in the database
{
let tmp_object = {};
tmpobject.id = dbobject.id;
validated_data.push(tmp_object);
}
})
}
resolve(validated_data);
}
}
What am I doing wrong? How can I wait for the database query to finish, then execute the main part? If there is only one validation, I could've used .then(). However, the list might have contained many elements and I need to wait for all of them to be verified.

Your Databaseobject.findOne() calls are asynchronous so your promise will resolve before any of them complete.
You can make use of Promise.all to wait until all of your promises resolve.
Hopefully, this will work for you:
router.post("/validate", (req, res) => {
validate_data(req.body.nottrusteddata)
.then(validated_data => {
const mydata = new Mydata({
id: req.body.id,
lst: validated_data
})
// Some useful stuff is here
res.status(200).json()
})
.catch(err => {
// Handle error
})
}
function validate_data(nottrusteddata) {
// Create array of pending promises
const promises = nottrusteddata
.map(item => {
return Databaseobject
.findOne({ 'id': item.id })
.exec()
})
// Wait for all promises to resolve
return Promise.all(promises)
.then(docs => {
return docs
.filter(dbobject => dbobject) // Filter out undefined
.map(dbobject => {
return { id: dbobject.id }
})
})
}
If you want, you could also use async-await here:
router.post("/validate", async (req, res) => {
try {
const validated_data = await validate_data(req.body.nottrusteddata)
const mydata = new Mydata({
id: req.body.id,
lst: validated_data
})
// Some useful stuff is here
res.status(200).json()
}
catch(err) {
// Handle error
}
})

Related

Why does my recieved from database data is undefined?

I`m creating API on my express.js server, so when it takes "get" request, some function placed in module file asking data from graph db:
module.js file:
function getGraphData() {
const cityName = 'Milan';
const readQuery = `MATCH (City {name: $cityName})-[:LINKED*1..3]-(city:City) RETURN city`;
const cities = [];
session
.run(readQuery, { cityName })
.then(function (result) {
result.records.forEach(function (record) {
cities.push({
title: record._fields[0].properties.name,
});
});
return cities;
})
.catch((err) => console.log(err));
}
module.exports.getGraphData = getGraphData;
After receiving data it stores in array named cities and looks like this:
cities: [ { title: 'City1' }, { title: 'City2' }, { title: 'City3' } ]
So, function is returning this array, and then I import function from module and use it in my router file:
const { getGraphData } = require('./../db/neo4j');
router.get('/', async (req, res) => {
try {
const p = await getGraphData();
console.log('p:', p); //p is undefined
//return res.status(200).send(p);
return res.status(206).json(p); // empty response, not any data only status code
} catch (e) {
console.log(e);
});
So, what I'm doing wrong? Why does api response is empty?
Im use debugger. Data realy comes to function in module, but doesn`t passing to api response to "p" variable.
Your getGraphData function is using .then . When it executes it makes the session call and returns immediately that is why it returns empty.
Although, you are doing await on getGraphData, your getGraphData function needs to be defined as async and use await with session for it to work.
async function getGraphData() {
const cityName = 'Milan';
const readQuery = `MATCH (City {name: $cityName})-[:LINKED*1..3]-(city:City) RETURN city`;
const cities = [];
try{
const result = await session.run(readQuery, { cityName });
result.records.forEach(function (record) {
cities.push({
title: record._fields[0].properties.name,
});
});
return cities;
}
catch(err){
console.log(err);
return err;
}
}

Multiple MongoJS calls in Node.js

I am working on a node.js which has multiple steps with Mongo:
Get a document
Push a subdocument into the subarray and save
Do an aggregation framework query to get the inserted subdocument
Return it to the calling function
My function:
addItem(itemId, data1, data2) {
return new Promise(function(resolve, reject) {
let item = Model.findOne({'_id': new ObjectID(itemId)});
resolve(item);
}).then(function(item) {
// STEP 2
const id = new ObjectID();
item.stuff.push({data1: .... })
item.save();
return { item: item, id: id }
}).then(function(res) {
return Model.aggregate([ .... ])
}).then(function(res) {
return res;
})
}
The problem is, I call my function addItem,
addItem(itemId, data1, data2).then(res => { PROBLEM })
where PROBLEM returns the call definition to my aggregation framework.
Example:
console.log(PROBLEM);
Aggregate {
_pipeline: [
.. items
],
_model: Model {},
options: {}
}
How do I fix it so my aggregation framework returns data? I think it has something to do w/ STEP 2, especially considering .save() returns a Promise
Ok, so it looks like I did the Promise and async / await backwards.
The solution:
In Express, my endpoint:
app.post('/endpoint', (req, res) => {
new Promise(resolve => {
addMessage(currentUserId, chatId, msg).then(res => {
console.log(res);
res.sendStatus(codes.CREATED).send(res);
});
});
});
and my addMessage function:
addMessage = async function (userId, chatId, message) {
let item = await Model.findOne({ .... });
let id = new ObjectID();
item.stuff.push({data1: .... });
await item.save();
let retval = await Model.aggregate([ .... ]);
return retval;
}

Express route still returning item deleted from mongodb

I have this route to delete a "garage" from the mongodb database and then grab some of the remaining garages. For some reason it is still returning the deleted garage and returning it, but if I check the database the delete was successful.
router.post('/garage/delete', requireLogin, async (req, res) => {
let limit = 20;
try {
let list = req.body;
list.map( async (item) => {
const existingGarage = await Garage.find({_id: item._id});
if (existingGarage) {
await Garage.deleteOne({_id: item._id});
} else {
res.status(400).send("Garage not found");
}
})
const allGarages = await Garage.find().limit( limit );
console.log(allGarages);
res.send(allGarages);
} catch {
res.status(400).send("Garage not found");
}
})
You will need to await all the promises returned by the map function.
Promise.all awaits an array of promises and runs them in parallel.
since you're passing an async function to the map function you will need to await all the promises returned by that async function
Other solution is to use a for of loop
router.post('/garage/delete', requireLogin, async (req, res) => {
let limit = 20;
try {
let list = req.body;
await Promise.all(list.map( async (item) => {
const existingGarage = await Garage.find({_id: item._id});
if (existingGarage) {
await Garage.deleteOne({_id: item._id});
} else {
res.status(400).send("Garage not found");
}
}))
const allGarages = await Garage.find().limit( limit );
console.log(allGarages);
res.send(allGarages);
} catch {
res.status(400).send("Garage not found");
}
})

Promise.all returning undefined in Node JS

I have a code to fetch directory names from first API. For every directory, need to get the file name from a second API. I am using something like this in my Node JS code -
async function main_function(req, res) {
const response = await fetch(...)
.then((response) => {
if (response.ok) {
return response.text();
} else {
return "";
}
})
.then((data) => {
dirs = ...some logic to extract number of directories...
const tempPromises = [];
for (i = 0; i < dirs.length; i++) {
tempPromises.push(getFilename(i));
}
console.log(tempPromises); // Prints [ Promise { <pending> } ]
Promise.all(tempPromises).then((result_new) => {
console.log(result_new); // This prints "undefined"
res.send({ status: "ok" });
});
});
}
async function getFilename(inp_a) {
const response = await fetch(...)
.then((response) => {
if (response.ok) {
return response.text();
} else {
return "";
}
})
.then((data) => {
return new Promise((resolve) => {
resolve("Temp Name");
});
});
}
What am I missing here?
Your getFilename() doesn't seem to be returning anything i.e it's returning undefined. Try returning response at the end of the function,
async function getFilename(inp_a) {
const response = ...
return response;
}
Thanks to Mat J for the comment. I was able to simplify my code and also learn when no to use chaining.
Also thanks to Shadab's answer which helped me know that async function always returns a promise and it was that default promise being returned and not the actual string. Wasn't aware of that. (I am pretty new to JS)
Here's my final code/logic which works -
async function main_function(req,res){
try{
const response = await fetch(...)
const resp = await response.text();
dirs = ...some logic to extract number of directories...
const tempPromises = [];
for (i = 0; i < dirs.length; i++) {
tempPromises.push(getFilename(i));
}
Promise.all(tempPromises).then((result_new) => {
console.log(result_new);
res.send({ status: "ok" });
});
}
catch(err){
console.log(err)
res.send({"status" : "error"})
}
}
async function getFilename(inp_a) {
const response = await fetch(...)
respText = await response.text();
return("Temp Name"); //
}

Express router not awaiting the forEach loop

Express router is not awaiting my forEach loop and sends the old unmanipulated object as a response instead of the new manipulated data.
Here I am using Sequalize as my ORM.
router.get('/', async (req,res) => {
try {
let trainings = await db.Training.findAll();
let locations = await db.Location.findAll();
await locations.forEach(location => {
trainings.forEach(training => {
if(location.trainingId == training.id){
training["location"] = location
}
})
})
res.status(200).json({
training:trainings
})
} catch(err) {
console.log(err);
res.status(404).json({
message : err
})
}
})
Basically you are using the await keyword against a synchronous process which is
locations.forEach(location => {
trainings.forEach(training => {
if(location.trainingId == training.id){
training["location"] = location
}
})
})
These lines of code doesn't return any promise or behave like a promise. So one solution can be having a function
function modify(trainings,locations){
return new Promise((resolve,reject)=>{
locations.forEach(location => {
trainings.forEach(training => {
if(location.trainingId == training.id){
training["location"] = location
}
})
})
resolve('done')
})
}
then have it like this
let trainings = await db.Training.findAll();
let locations = await db.Location.findAll();
await modify(trainings,locations)
or you can simply remove the await keyword from your current state of code.

Resources