Chained async calls returning undefined response - node.js

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.

Related

Question about the syntax of a function in Node.js

I am following a tutorial to make a blog, and for the MongoDB connection in the server.js file, the instructor made a boiler connection function withDB. Operations and res are props of withDB function. In line 6, is operations a function passed a prop of the withDB functions?
Below is the withDB function.
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('my-blog');
await operations(db); // is operations a function that takes db as its props?
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error });
}
}
Using withDB in a function
app.get('/api/articles/:name', async (req, res) => {
withDB(async (db) => {
const articleName = req.params.name;
const articleInfo = await db.collection('articles').findOne({ name: articleName })
res.status(200).json(articleInfo);
}, res);
})
yes actually operations is your callback function, you call it with db as param once you initialize your database connection.
Maybe you're not comfortable with ES6 arrow function syntax. you can find in Mdn doc a simple example with old regular function, and in your case it could be :
function findArticlesByName(articleName) {
return function(db) {
return db.collection('articles').findOne({ name:
articleName });
}
}
async function withDB(callback) {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('my-blog');
return callback(db);
} catch (error) {
throw new Error({ message: 'Error connecting to db', error });
} finally {
client?.close();
}
}
app.get('/api/articles/:name', async (req, res) => {
try {
const articleInfo = await withDB(findArticlesByName(req.params.name));
res.status(200).json(articleInfo);
} catch(error) {
res.status(500).json(error);
}
})
Conclusion, you could easily inline your callback function like in your example, but maybe it's more understandable that way.
Moreover, you should avoid to use a wrapper in order to create and close your db connection after each request, because it could occur some weird errors. Databases connections or any resource-intensive tasks should be shared as much as possible.
So a better solution is to create a specific class with the default implementation of your singleton, construct a single instance of your connection at the top of your app, pass the single instance into each module that needs it then close your connection just before exiting your app.
Hope it'll help.

node.js function returns Promise { <pending> }, why?

I'm beginner in nodejs, This function returns "Promise {pending}
" I Appreciate any help.
Thanks
async function getCurrentUser(id){
try{
let user = User.findOne({id:id})
return user;
}catch(err){
return err;
}
}
You missing the await in front of some function that return a Promise
let user = await User.findOne({id:id})
Promise is an immutable async operation that need time to finish so you either use async/await like you did or chain the promise with .then call like
User.findOne({ id })
.then((response) => {
// do sth with the resposne
});
recommend you to read Async & Performance from YDKJS series
full rewrite of code:
async function getCurrentUser(id) {
try {
let user = await User.findOne({ id: id })
return user;
} catch (err) {
return err;
}
}
// getCurrentUser usage
async function doSth(){
let user = await getCurrentUser();
}
usage clarification:
putting async in front of a function make this function return type a promise no matter what you return inside it so the only two way to use such a function is just like how you use a promise either use it in another async function using await or use .then() chaining function
u missing await
async function getCurrentUser(id){
try{
let user = await User.findOne({id:id})
return user;
}catch(err){
return err;
}

is there a way to return data from async/await in nodejs

I was trying to use async/await functionalities and my problem is that I have an async function which I need to return some data from and inside it I nested 3 await functions where the last one returns the value for the whole function, but I could not do that because the last await is on find query from mongodb and it returns the found element only. So I wanted to know if there is some way to to that data from that await function.
async register_Employee_Credential (id,req,res){
try{
let employee_credential= new Employee_credential({
employee: id,
username: req.body.username,
password: req.body.password
});
await bcrypt.genSalt(10,async (err,salt)=>{ //first await function
await bcrypt.hash(employee_credential.password,salt, async (err,hash)=>{ //second await function
if(err) console.log("error while generating salt");
employee_credential.password = hash;
result = await Employee_credential.create(employee_credential,async (err,result)=>{ // third await function
if(err)
{
var errMessage = await help.Property_Validator(err);
return errMessage; // this is the return message i need
}
})
})
})
return errMessage; //this is the final return for the calling function
}catch(err){
console.log("employee creditial error furthur: " + err);
}
}
An async function ALWAYS returns a promise. That is built into the language and you cannot change it. So, you can never return a plain value from an async function.
Further a plain asynchronous callback in your function gets called long AFTER the function has already returned that promise so you cannot return a value from the function from within that callback.
Further yet, await only does anything useful when you are awaiting a promise so your await in this await Employee_credential.create() does nothing useful. The same with each of your other await statements. They are not awaiting promises so they do nothing useful. Please read about how await actually works and is used rather than just sticking it places hoping it solves some problem. It only works properly when used in a very specific way. You need to learn that.
And, finally, you can NEVER return an asynchronously retrieved value directly from a function. Javascript just doesn't work that way. You will have to communicate back the return value via a callback, a promise or an event.
You will need to either promisify all asynchronous functions within your async parent function so that you can use await with them or you will need to stop using async/await and communicate back the return result via a callback.
Since none of your three asynchronous operations in your function directly support promises, then the least amount of change to your code woudl be to add a callback argument to your function and go the traditional callback method of communicating back asynchronous results. You then pass the callback into the function and get the result inside that callback when it is called.
register_Employee_Credential(id, req, res, callback) {
try {
let employee_credential = new Employee_credential({
employee: id,
username: req.body.username,
password: req.body.password
});
bcrypt.genSalt(10, async (err, salt) => {
if (err) {
console.log(err);
return callback(err);
}
bcrypt.hash(employee_credential.password, salt, async (err, hash) => {
if (err) {
console.log(err);
return callback(err);
}
employee_credential.password = hash;
Employee_credential.create(employee_credential, async (err, result) => {
if (err) {
var errMessage = help.Property_Validator(err);
callback(errMessage);
} else {
callback(null, result);
}
})
})
})
} catch (err) {
console.log("employee creditial error furthur: " + err);
callback(err);
}
}
If you want to use promises, then bcrypt appears to already have a promise interface built in if you do NOT pass it a callback so you just have to promisify the .create() method and can do that like this:
const {promisify} = require('util');
async register_Employee_Credential(id, req, res) {
let employee_credential = new Employee_credential({
employee: id,
username: req.body.username,
password: req.body.password
});
// promisify the create method
Employee_credential.createP = promisify(Employee_credential.create);
let salt = await bcrypt.genSalt(10);
let hash = await bcrypt.hash(employee_credential.password, salt);
employee_credential.password = hash;
try {
let result = await Employee_credential.createP(employee_credential);
return result;
} catch(err) {
let errMessage = help.Property_Validator(err);
throw errMessage;
}
}
You would use the async/promise version by calling:
register_Employee_Credential(id, req, res).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});

Why is async functions return Promise <pending> error in console

I want to use the async function to bring out a particular value from my database to my the function global so I can use it in other parts of my application.
async function dimension() {
const result = await Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
console.log(holder) /// this print the expected result, that i want to make global
});
return {
result
};
};
console.log(dimension())
but the console.log of the dimension() gives me this
Promise { <pending> }
instead of the same value that
console.log(holder)
gives me nothing.
The problem is you are printing the result of dimension() as soon as you call it, but since this function is async, it returns a promise that is not yet resolved.
You do not need to use async/await here. Settings.find() seems to return a Promise. You can just return directly this Promise and use .then() to do something once that promise is resolved.
Like this :
function dimension () {
return Settings.find({ _id: '5d7f77d620cf10054ded50bb' }, { dimension: 1 }, (err, res) => {
if (err) {
throw new Error(err.message, null);
}
return res[0].dimension;
});
}
dimension().then(result => {
//print the result of dimension()
console.log(result);
//if result is a number and you want to add it to other numbers
var newResult = result + 25 + 45
// the variable "newResult" is now equal to your result + 45 + 25
});
More info on Promises and async/await
You have to await for your result, like this:
const result = await dimension();
console.log(result);
In that case, you don't even to make the original function async, just write it like this:
function dimension() {
return Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
});
};
async function myGlobalFunc() {
const result = await dimension();
console.log(result);
}
The best way to have this globally available is to just put your function dimension in a file somewhere. Then where you need the value, you just require it and await its value. E.g.
// get-dimension.js
// ...const Settings = require... comes here
module.exports = function getDimension() {
return Settings.find({_id : "5d7f77d620cf10054ded50bb"},{dimension:1}, (err, res) => {
if(err) throw new Error(err.message, null);
const holder = res[0].dimension;
return holder;
});
}
// your other modules, e.g.
// my-service-handler.js
const getDimesion = require('./get-dimension');
async function myServiceHandler() {
const dimension = await getDimension();
// do stuff with dimension.
}
You're using async/await, but you're mixing it with callbacks, this is not desirable as it leads to confusion. It's not clear what you expect to happen in the callback, but return holder; likely doesn't do what you expect it to do, returning from a callback does not work the same way returning from a promise handler works. Your entire implementation should work with promises so that the async/await syntax reads more naturally (as it was intended).
async function dimension() {
// We're already awaiting the result, no need for a callback...
// If an error is thrown from Settings.find it is propagated to the caller,
// no need to catch and rethrow the error...
const res = await Settings.find({_id: "5d7f77d620cf10054ded50bb"}, {dimension: 1});
return {result: res[0].dimension};
}
(async () => {
try {
console.log(await dimension());
} catch (err) {
console.error(err);
}
})();
Use dimension().then() in your code then it will work fine.
async function globalDimension() {
const data = await Users.findOne({ phone: 8109522305 }).exec();
return data.name;
}
globalDimension().then(data => {
console.log(data);
});

Executing Multiple Sequelize JS model query methods with Promises - Node

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.

Resources