Express + Mongoose: async/await returning undefined - node.js

I'm trying to separate route code from database codes, but I'm stuck with an error when the route make an call to controller method.
I have the productRoutes.js:
router.route('/')
.get(async (req, res, next) => {
try {
let criteria = {};
for (const el in req.query) {
criteria[el] = req.query[el];
}
console.log('Getting all products', criteria);
const result = await controller.getAll(criteria);
console.log('router result:', result);
const status = (result.ok ? (result.count > 0 ? 200 : 404 ) : 400);
return res.status(status).send(result);
} catch (err) {
return next(err);
}
});
The productController.js:
exports.getAll = (criteria) => {
model.find(criteria, '-__v').exec((err, records) => {
const ok = (err ? false : true);
let result = {}
result.ok = ok;
if (ok) {
result.data = records;
result.count = records.length;
} else {
result.count = 0;
result.err = err;
}
console.log('controller result count:', result.count);
return result;
});
}
When executed it produces this:
Getting all products {}
router result: undefined
TypeError: Cannot read property 'ok' of undefined
at router.route.post.get (C:\dev\projects\mercante\server\routes\productRoutes.js:58:36)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
controller result count: 4
The execution isn't waiting for controller.getAll(criteria).
I've tried some codes I found looking for similar questions, like try/catch the async call, but didn't works or I missed some detail.
Thanks in advance.

The reason: Your controller's getAll method doesn't return any value.
You should do smth like this:
exports.getAll = async (criteria) => {
// Return value!
return await model.find(criteria, '-__v');
}

Thanks, #Rashad. Now it works:
productController.js:
exports.getAll = async (criteria) => {
return await model.find(criteria, '-__v');
}
productRoutes.js:
.get(async (req, res, next) => {
try {
let criteria = {};
for (const el in req.query) {
criteria[el] = req.query[el];
}
console.log('Getting all products', criteria);
let result = {};
const data = await controller.getAll(criteria);
result.count = data.length;
result.data = data;
const status = (result.ok ? (result.count > 0 ? 200 : 404 ) : 400);
return res.status(status).send(result);
} catch (err) {
return next(err);
}
});

Related

How to return 2 arrays after saving data to mongodb using node js

I need help with code below. I get an array of items from the client then the goal is to save them in mongodb and return the list classified as 'saved' and 'failed' items. sample of failed items are those that are duplicate on a unique attribute.
I know the code below will not work because of variable scope. how do i get around it? the code below returns an empty array for both savedItems and failedItems. Thanks!
router.post('/addItems', async (req, res, next) => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
function saveData() {
for (i = 0; i < items.length; i++) {
item = items[i];
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
});
}
return result;
}
saveData().then(result => {
res.send({
results: result
});
});
});
router.post('/addItems', async (req, res, next) => {
// use try catch when use async
try {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
for (let i = 0; i < items.length; i++) {
const item = items[i];
// use the returned promise instead of callback for Model.create
const data = await Model.create({ ...item });
result.savedItems.push(item);
// if also need to handle failed item in result use anathor try catch inside
/*try {
const data = await Model.create({ ...item });
result.savedItems.push(item);
} catch( err ) {
result.failedItems.push(item);
}*/
}
res.send({
results: result
});
} catch( err ) {
// To all the errors unexpected errors + thrown rejected promises
res.send({
error: err
});
}
});
Your saveData method didn't return a promise, try this
function saveData() {
return new Promise(resolve => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
let promises = [];
for (i = 0; i < items.length; i++) {
item = items[i];
let promise = new Promise(resolve => {
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
resolve();
});
});
promises.push(promise);
}
Promise.all(promises).then(() => resolve(result));
})
}

export module return object with added method

I have the following codes :
app.post("/login/auth", (req, res) => {
(async function() {
let NikitaBellucci = (await auth.login(req, db, crypto))[0];
res.send(NikitaBellucci);
})();
});
and
exports.login = (req, db, crypto) => {
pro = new Promise((resolve,reject) => {
let pseudo = req.body.pseudo;
let password = crypto.createHmac('sha256', req.body.password)
.update('jojofags suck')
.digest('hex');
let query = "SELECT * FROM users WHERE users.pseudo = ? AND users.password = ? LIMIT 1";
db.query(query, [pseudo, password], function (err, result) {
if (err) throw err; // GESTION D'ERREURS
result.isAdministrator = function() {
if(this.role <= 90) { return true; } else { return false; }
}
resolve(result);
});
})
return pro.then((val) => {
console.log(val);
return val;
})
}
On console.log(val);, I can see the previously added method to my object. But when returning it to my main file, method "disappear", how to avoid that?
thank you
Your function is attached to the entire result object, but you get the 0 property of it in (await auth.login(req, db, crypto))[0]; which won't have the function. Just remove the [0] and NikitaBellucci.isAdministrator will be the function in question.

Formatting MongoDb response for API

I'm trying to retrieve a collection in MongoDb and the problem is the API already responds even though the processing is not yet done. So basically what I'm trying to do is retrieve the results using find, iterate through the results using foreach, and push each row to an array.
I've tried a variety of suggestions but none work so far. Below is a rough idea of what I'm trying to pull off.
get: async function (req, res, next) {
let messagesArray = []
let sessionId = req.query.session
client.connect(err => {
try{
const collection = client.db("xxxxxxx").collection("xxxxxxx")
let results = collection.find({},{sessionId:sessionId})
for (const result of results){
order = {"text" : order.partner+", "+order.order+ ", "+order.quantity}
messagesArray.push(order)
}
}
catch(e){
}
client.close()
res.send(200,{messages:messagesArray})
})
},
for loop will execute asynchronous so before your for loop is getting finished res is sent so try for the following
get: async function (req, res, next) {
let messagesArray = []
var counter = 0;
let sessionId = req.query.session
client.connect(err => {
try{
const collection = client.db("xxxxxxx").collection("xxxxxxx")
let results = collection.find({},{sessionId:sessionId})
for (const result of results){
order = {"text" : order.partner+", "+order.order+ ", "+order.quantity}
messagesArray.push(order)
counter++;
if(counter == results.length) {
client.close()
res.send(200,{messages:messagesArray})
}
}
}
catch(e){
}
})
},
use await to wait for result to available :
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
get: async function (req, res, next) {
let messagesArray = []
let sessionId = req.query.session
const client = await MongoClient.connect(url, { useNewUrlParser: true }).catch(err => { console.log(err); });
if (!client) { return;}
try{
const collection = client.db("xxxxxxx").collection("xxxxxxx")
let results = await collection.find({sessionId:sessionId})
console.log(results);
// ...
}
catch(e){
console.log(e);
}
finally {
client.close();
}
},

undefined result nodejs async await

i'm testing await functions but i have an "undefined" result when i check the return of the async function, my express function is like this (i removed unnecesary code, just to show how i am using the async
const getAppsConsumptionSum = async (msisdn, startPeriod, endPeriod) => {
var urlTigoPlus = 'http://...';
var args = {
requestConfig: {
timeout: config.get('localServer.remoteTimeout')
}
};
remoteApi = await restClient.get(url, args,
async (data, response) => {
if (response.statusCode === 200) {
sumatoria = await group(data.arrayofdata).by('subapplication').reduce(async function(id, entries) {
return {
appname: id,
mb: (entries.map(getBytes).reduce(add)) / 1048576
};
});
return sumatoria;
} else {
next(utils.error(503));
}
}
);
};
exports.dataAppsConsumption = async function(req, resp, next) {
let prepaidQuery = 'select ...';
const resultPrepaid = await clientDseDev.execute(prepaidQuery)
.then(async resultPrepaid => {
sumatoria = await getAppsConsumptionSum(variable1, startPeriod, endPeriod);
console.log('this variable shows undefined ' + sumatoria)
//i tried also with this
getAppsConsumptionSum(variable1, startPeriod, endPeriod).then((sumatoria) => {
console.log('this variable shows undefined ' + sumatoria)
});
})
.catch((err) => {
console.log(err)
});
};
thanks all for your help, #jfriend00 solution was right, also i had to add a return before the call to the axios function
return axios.get(url, options
)
.then(async function (response) {
if (response.status === 200){
group(response.data.arrayofdata).by('subapplication').reduce(function(id, entries) {
return {
appname: id,
mb: (entries.map(getBytes).reduce(add)) / 1048576
};
});
return sumatoria;

Last then function being called multiple times

I'm trying to write a chain of Promises but the last .then() is being called multiple times and I don't know why. The last .then() must run a single time because it will call another API passing result as body.
I know that is being called multiple times because I'm logging as console.log().
What is wrong on my code? For my understand, then() should wait promise returns something.
app.post('/router/join', function(req, res){
let data = req.body;
sessions.validate(data)
.then(result => {
return {
authenticated: (result.code === 201)
};
})
.then(result => {
if(result.authenticated){
return contacts.getContacts(data.tenant_id).then(cs => {
let json = merge(result, cs.data);
return Promise.all(cs.data.items.map(contact => {
return messages.getLastMessage(data.tenant_id, contact.item.contact_id, data.hash_id)
.then(result => {
contact.item.last_message = result.code === 200 && result.data.length > 0 ? result.data[0] : null;
return contact;
});
})).then(result => {
json.items = result;
return json;
});
});
} else {
return result;
}
})
.then(result => {
//this call should run after all other promises and only a single time
let event = result.authenticated ? 'valid_session' : 'invalid_session';
console.log('222');
proxy.send(event, result)}
)
.catch(err => {
console.log('333');
proxy.send('invalid_session', {socket_id: data.socket_id})
})
res.status(201).send({});
});
You can use async/await to clean it up. Inside async functions you can await the results of promises.
app.post('/router/join', async function (req, res, next) {
try {
let data = req.body;
let {code} = await sessions.validate(data);
let result = { authenticated: (code === 201) };
if (result.authenticated) {
let cs = await contacts.getContacts(data.tenant_id);
let json = merge(result, cs.data);
let items = Promise.all(cs.data.items.map(async contact => {
let result = await messages.getLastMessage(data.tenant_id, contact.item.contact_id, data.hash_id)
contact.item.last_message = result.code === 200 && result.data.length > 0 ? result.data[0] : null;
return contact;
}));
json.items = items;
result = json;
}
let event = result.authenticated ? 'valid_session' : 'invalid_session';
console.log('222');
proxy.send(event, result);
res.status(201).send({});
} catch (err) {
proxy.send('invalid_session', {socket_id: data.socket_id})
next (err);
}
});

Resources