Executing Multiple Sequelize JS model query methods with Promises - Node - node.js

I am having a problem retrieving data from database using sequelize js. I am new to NODEJS. I don't know if Promise and Promise.all are built in functions
So i install and require npm promise in my code too.
Below is my code.
var Promise = require('promise');
var user_profile = new Promise(function(resolve, reject) {
db.user_profile.findOne({
where: {
profile_id: new_profile_id
}
}).then(user => {
console.log('Summary Result User found.');
resolve(user);
});
});
var all_reports = new Promise(function(resolve, reject) {
db.report.all().then(reports => {
console.log('Summary Result Reports found.');
resolve(reports);
});
});
var report_details = new Promise(function(resolve, reject) {
db.report_detail.findAll({
where: {
profile_id: new_profile_id
}
}).then(report_details => {
console.log('Summary Result Report Details found');
resolve(report_details);
});
});
var all_promises = Promise.all([user_profile, all_reports, report_details]).then(function(data) {
console.log('**********COMPLETE RESULT****************');
console.log(data);
}).catch(err => {
console.log('**********ERROR RESULT****************');
console.log(err);
});
I want to get the data of all three queries. When i run them individually I get the data but when i run them in Promise.all I only get user_profile data and other two remain undefined
I have also tried nested these queries with .then but result is still same I only get one query data other two remain undefined
with then chainging
var results = [];
var new_profile_id = req.params.profile_id;
console.log(new_profile_id);
db.user_profile.findOne({
where: {
profile_id: new_profile_id
}
}).then(user => {
console.log('Summary Result User found.');
results.push(user.dataValues);
return user;
}).then(user => {
db.report.all().then(reports => {
console.log('Summary Result Reports found.');
results.push(reports.dataValues);
return reports
});
}).then(reports => {
db.report_detail.findAll({
where: {
profile_id: new_profile_id
}
}).then(report_details => {
console.log('Summary Result Report Details found');
results.push(report_details.dataValues);
console.log('**********COMPLETE RESULT****************');
console.log(results);
console.log('**********COMPLETE RESULT****************');
return report_details;
});
});
can someone please help me in this concept what i am doing wrong.
Thanks

The latest version of Node.js already supports Promise natively, so does Sequelize. This means no need to require promise separately.
The following code is based on yours.
const user_profile = db.user_profile.findOne({
where: {
profile_id: new_profile_id
}
});
const all_reports = db.report.all();
const report_details = db.report_detail.findAll({
where: {
profile_id: new_profile_id
}
});
Promise
.all([user_profile, all_reports, report_details])
.then(responses => {
console.log('**********COMPLETE RESULTS****************');
console.log(responses[0]); // user profile
console.log(responses[1]); // all reports
console.log(responses[2]); // report details
})
.catch(err => {
console.log('**********ERROR RESULT****************');
console.log(err);
});
Notice that there is no need to wrap Sequelize calls with Promise becasue Sequelize already returns promises. This way you only need to have one catch in the last Promise.all() which you were missing in all calls. What this means is that if any calls were to fail, a corresponding resolve would never be called. This, in turn, means that the last Promise.all() would also never be called. This is why it is good to handle all errors at the end once, unless there are some custom error handling requirements.

Related

Chained async calls returning undefined response

I seem to be getting undefined from my async code, and none of the answers I have found seem to work.
So my setup is router calls a controller, which calls a service, which calls a database. The controller is like so (removed some validation etc to reduce code)
const { userService } = require('../services/index.services');
const postUser = async (req, res) => {
try {
const user = {
user_id: req.body.userId,
user_status: req.body.userStatus,
created_at: new Date(),
updated_at: new Date(),
};
const result = await userService.createUser(user);
console.log(result);
} catch (err) {
return apiResponse.ErrorResponse(res, err);
}
};
module.exports = {
postUser,
};
You can see I have a console log of the result. Then my service class is like so
const { addUserToDb } = require('../database/users.db');
const createUser = async (user) => {
try {
const createdUser = await addUserToDb(user);
console.log(createdUser);
} catch (err) {
throw new Error(err);
}
};
module.exports = {
createUser,
};
That one also has a console log. Then finally users.db
const pool = require('./database');
const addUserToDb = (user) => {
pool.getConnection((err, connection) => {
if (err) {
throw new Error(err);
}
connection.query('INSERT INTO `users` SET ?', user, (err) => {
if (err) { throw new Error(err); }
pool.releaseConnection(connection);
return user;
});
});
};
module.exports = {
addUserToDb,
};
So you can see this adds the user to the database and returns the user. So my user is successfully being inserted into the database.
My question is why am I getting undefined in both my console logs? I am returning from all modules which is where I noticed a lot of other people had issues because of no returns.
So can someone explain why these may be undefined?
Thanks
It's because in your service class you have,
const createdUser = await addUserToDb(user);
But addUserToDb in users.db doesn't return a promise.
addUserToDb is a regular function that executes the query using callbacks.
Hence, even though you have return user; nested within the 2nd callback, it never returns the user value back to createUser in the service class as callbacks are asynchronous.
In fact, addUserToDb function in users.db doesn't wait for the callback to complete and thus returns undefined back to the service class.
And hence you see undefined being logged in your service class.
async/await only works with promises, not callbacks.
You need to refactor ./database module to use promises instead of callbacks for pool.getConnection and connection.query.
I use mysql2 npm module as it has a promise wrapper,
https://www.npmjs.com/package/mysql2#using-promise-wrapper
And the reason why console.log(result); in your controller is undefined is because you don't return anything from your service class.
You need to add return createdUser; in your service class's createUser method.

file write issue in Async/Await

Here I am trying to retrieve objects and push them into the array. For some reason there is only one record being pushed into the file when it should contain more objects. Can you help me out with this or let me know where I am going wrong? Here is my code:
exports.createjson = (req, res, next) => {
try {
var myPromise = () => {
// ...
};
var callMyPromise = async () => {
const responsearray = [];
var result = await myPromise();
return new Promise((resolve, reject) => {
result.forEach(element => {
NewsModel.findOne({ _id: element.newsId }).exec(
async (err, result) => {
if (err) {
throw err;
}
reportsModel
.findOne({
$and: [
{ userId: req.query.userId },
{ newsId: element.newsId }
]
})
.exec((err, newsResult) => {
if (err) {
throw err;
}
// console.log(newsResult);
var response = {
newsId: element.newsId,
title: result.title,
collection: result.group,
belivibalityIndex: element.belivibalityIndex,
priorknowledge: element.priorknowledge,
readingTime: element.readingTime,
userId: element.userId,
comment: element.comment,
report: newsResult !== null ? newsResult.feedback : null
};
// #all object pushed and displayed in console
responsearray.push(response);
console.log(response);
console.log(responsearray.length);
// let data = JSON.stringify(responsearray);
// #here is the issue // fs.writeFileSync("abc.json", data, null, null, flag = 'a');
return responsearray;
});
}
);
});
});
};
callMyPromise().then(function(responsearray) {
res.json(responsearray);
});
} catch (error) {
next(error);
}
};
You're not quite using Promises properly. For example, you create a Promise object but never call the resolve/reject functions. In the forEach loop you are calling functions that use callbacks and when that work is done you can resolve the promise you're wrapping it in.
Also you're calling res.json and writing the file (though it's commented out) while you're in the forEach loop. That means res.json will get called multiple times, which is not allowed. You can only have one response from an http request.
I restructured the code so that it collects each promise in an array of Promises then waits for all of them to resolve. Only after all of the work is done, we can write the file and call res.json to complete the http request.
exports.createjson = async (req, res, next) => {
const responsearray = [];
var elements = await myPromise();
var promises = []; // collect a bunch of promises to wait on
elements.forEach(element => {
// one promise per element that resolves when response is on the array
var promise = new Promise(function(resolve, reject) {
NewsModel.findOne({ _id: element.newsId }).exec((err, result) => {
if (err) { return reject(err); }
reportsModel
.findOne({
$and: [{ userId: req.query.userId }, { newsId: element.newsId }]
})
.exec((err, newsResult) => {
if (err) { return reject(err); }
var response = { /* response body */ };
responsearray.push(response);
console.log(response);
console.log(responsearray.length);
// complete the promise now that the response is on the array
return resolve();
});
});
});
// collect each promise in an array so we can wait for them all
promises.push(promise);
});
// wait for all the work to complete
await Promise.all(promises).catch(err => next(err));
// write the responsearray to a file as json
let data = JSON.stringify(responsearray);
fs.writeFileSync("abc.json", data);
return res.json(responsearray);
};
I also removed the try/catch block since the Promise allows you to use .catch in a cleaner way. It simplifies the nesting which makes it easier to read.
The key takeaway here is the general structure:
// get your array to work with
var array = await someFunction()
var manyPromises = []
var manyResults = []
// for each thing in the array create a promise
array.forEach( thing => {
manyPromises.push( new Promise((resolve,reject) => {
doSomething(thing, (err, result) => {
if (err) return reject(err);
// store the results in the array and resolve the promise
manyResults.push(result)
return resolve();
});
});
});
// wait for all promises in manyPromises to complete
await Promise.all(manyPromises).catch(err => return next(err));
// now the many promises are done and manyResponses are ready
saveResponsesToFile(JSON.stringify(manyResponses))
return res.json(manyReponses)

How to refactor promises all chain using async await Node js and sequelize ORM

I want to refactor code for chain of promises by async, await. I have sequelize ORM for DB management and the code is written in AWS Lambda function having multiple middleware. In such cases I have to traverse code for multiple entries using sequelize transactions. It is easy to manage using promise.all() but need to change it to async await syntax for cleaner code.
Here are my demo code.
/* get all invoice where user_id === current logged in user, and where status != "paid" */
db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: "paid"
}
}
}).then(invoices => {
if (!invoices || invoices === null) {
return false;
}
function addScheduledTransactionAttempts(invoice, tryPayOnDate, t) {
return new Promise((resolve, reject) => {
/* check If any ScheduledTransactionAttempts exists for this invoice.id */
db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id
}
})
.then(function(attempts) {
if (attempts) {
attempts
.destroy({}, {
transaction: t
})
.then(deletedAttempts => {
console.log("Attempts Record Deleted: ", deletedAttempts);
})
.catch(error => {
reject(error);
t.rollback();
});
}
return db.ScheduledTransactionAttempts.create({
invoice_id: invoice.id,
payment_source_id: PaymentMethodId,
try_pay_on_date: tryPayOnDate,
stripe_customer_id: currentLogInStripeCustomerId
}, {
transaction: t
})
.then(function(attempt) {
resolve(attempt.id);
})
.catch(error => {
reject(error);
t.rollback();
});
})
.catch(error => {
reject(error);
t.rollback();
});
});
}
//Run transaction to addScheduledTransactionAttempts
return db.sequelize.transaction().then(function(t) {
let promiseArr = [];
var i = 0;
invoices.forEach(function(invoice) {
var schedulePaymentDate = moment(paymentDate);
if (invoice) {
let tryPayOnDate = schedulePaymentDate
.add(i, "month")
.format("YYYY-MM-DD");
promiseArr.push(
addScheduledTransactionAttempts(invoice, tryPayOnDate, t) //calling above function
);
i++;
}
});
//now execute promise all
Promise.all(promiseArr)
.then(function(result) {
t.commit();
return true;
})
.catch(function(err) {
t.rollback();
return false;
});
});
});
In the above code I want to change
Promise.all(promiseArr)
which is calling
addScheduledTransactionAttempts
function to do DB queries to simple async function await process to make it easy simpler understandable without having multiple .then or .then inside then promises.
Any help regarding would be appreciated,
Thanks.
It's quite simple. await is valid when invoking methods that return a Promise. All of your SDK methods already return a promise, so refactoring should be quite straight forward. Here's something to get you off ground:
const processInvoices = async currentLoggedInUser {
const invoices = await db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: 'paid',
},
},
});
if (yourOwnLogicForInvoicesObject) {
for (const invoice of invoices) {
const potentiallyFoundInvoice = await db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id,
},
});
if (potentiallyFoundInvoice) {
await addScheduledTransactionAttempts(potentiallyFoundInvoice)
}
}
}
}
const addScheduledTransactionAttempts = async invoice => {
console.log('Do something with your invoice', invoice)
}
Long story short: refactor the code inside your functions into smaller functions and make these new functions async, just like I did with addScheduledTransactionAttempts and processInvoices
More on async/await

For loop issue in MEAN node jS

I am trying to send the response when the loop k value is equal to users[0].employees.length, but it's directly moving forward to max length, how do I solve it
Userlist.find(queryObj).exec(function (err, users) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
}
else {
console.log('found users list', users);
// res.json(users[0].employees);
var k=0;
var resparr=[]
for(var i=0;i<users[0].employees.length;i++){
k++;
User.find({"_id":users[0].employees[i]._id}).exec(function(err,user){
resparr=resparr.concat(user);
console.log("resparray:",k,resparr.length,i,users[0].employees.length)
if(k==users[0].employees.length)
{
console.log("success",resparr)
res.json(resparr);
}
})
}
}
});
You are using an asynchronous function call within a synchronous loop. The loop will always finish before your callback functions are called.
You will need some kind of asynchronous looping. For a native solution you can use Promises:
const getUser = (id) => {
return new Promise((resolve, reject) => {
User.find({ '_id': id })
.exec((err, user) => {
if (err) reject(err);
else resolve(user);
});
});
};
const promises = users[0].employees.map(({ _id: id }) => getUser(id));
Promise.all(promises)
.then(users => res.json(users))
.catch(err);
For utility packages in node.js, I'd recommend the package async for a callback based solution or bluebird for Promises. Both have a .map function which is perfect for this use case.

Node.js Bluebird Promise: promise.any to return default image

Using Bluebird promises in a node.js environment, I check if any or none of three possible variations of a file exists (image.png/image.gif/image.jpg) on my server. If one of those three files exists, it shall be returned to the user. If none of the three files exist, a default image shall be returned. So far, I came up with this code:
var promise1 = new Promise(function(resolve, reject) {
fs.statAsync('./image.jpg')
.then(function(result) {
if(result){
resolve('jpg');
}
}).catch(function(err){
//foo
});
});
var promise2 = new Promise(function(resolve, reject) {
fs.statAsync('./image.gif')
.then(function(result) {
if(result){
resolve('gif');
}
})
.catch(function(err){
//foo
});
});
var promise3 = new Promise(function(resolve, reject) {
fs.statAsync('./image.png')
.then(function(result) {
if(result){
resolve('png');
}
})
.catch(function(err){
//foo
});
});
Promise.any([
promise1,
promise2,
promise3
]).then(function(result) {
res.sendfile("./image."+result);
}).catch(function(err){
res.sendfile("./default.png");
});
Problem is: as long as one of those three files exists, everything works just fine. But I can't get it to work to have the default image to be returned in case none of the three files can be found.
statAsync already returns a Promise, so you don't need to create your own (doing so is an antipattern). If you simply map the resolved Stats value to the value you want, you won't need to worry about resolve and reject calls, etc.
var promise1 = fs.statAsync('./image.jpg')
.then(function () { return 'jpg'; });
var promise2 = fs.statAsync('./image.gif')
.then(function () { return 'gif'; });
var promise3 = fs.statAsync('./image.png')
.then(function () { return 'png'); });
Promise.any([
promise1,
promise2,
promise3
]).then(function(result) {
res.sendfile("./image."+result);
}).catch(function(err){
res.sendfile("./default.png");
});
As mentioned in a comment posted against your question, your code is not working because, when statAsync fails, you are not rejecting the Promise you created, but you should not need to create your own Promise in the first place.

Resources