I have a simple custom module in which all methods are supposed to return database records.
I am getting all records after running a query but when I try to assign those records to some variable then it says null. Not sure what is going on.
Here is my custom module code:
module.exports = {
mydata: {},
all: function (req, res, next) {
var posts = null;
con.query("SELECT post_id,title, body from `posts`", function (err, rows, fileds) {
if (err) {
console.log('Error' + err);
}
posts = rows[0]; // assigned object object to posts but not working
console.log('names' + posts);
});
console.log('my posts' + posts); // it says null/empty
return posts; // empty return
},
I am calling all methods like this in my route:
console.log("admin.js :" + postModel.all());
All are empty or null. Please guide or suggest. Am I missing anything?
Try using async & await plus it's best practice to wrap the code in try catch, any pending promise will be resolved at the .then method or any error will be caught at the .catch of the caller/final function:
/ * Handler (or) Service (or) Whatever */
const FetchDataFromDB = require('../');
/* caller Function */
let fetchDataFromDB = new FetchDataFromDB();
fetchDataFromDB.getDataFromDB()
.then((res) => console.log(res))
.catch((err) => console.log(err))
/* DB Layer */
class FetchDataFromDB {
/* Method to get data from DB */
async getDataFromDB() {
const getQuery = "SELECT post_id,title, body from `posts`";
try {
let dbResp = await con.query(getQuery);
if (dbResp.length) {
//Do some logic on dbResp
}
return dbResp;
} catch (error) {
throw (error);
}
}
}
module.exports = FetchDataFromDB;
Welcome my friend to the world of Asynchronous function.
In your code the
console.log('my posts' + posts); and return posts;
are executing before the callback assigns values to the posts variable.
Also restrict yourself from using var instead use let for declaring variable works better without error for scoped functions.
Here below:
The async keyword declares that the function is asynchronous.
The await keyword basically says that lets first get the result and then move on to next statement/line. All awaits should be done inside an async functions only.
module.exports = {
mydata: {},
all: async function (req, res, next) { //Let this be an asynchronous function
try{
let [rows, fields] = await con.query("SELECT post_id,title, body from `posts`");
//let us await for the query result so stay here until there is result
let posts = rows[0]; // assign first value of row to posts variable now
console.log('my posts' + posts);
return posts;
}catch(err){
return err;
}
},
}
Please take time reading up nature of Asynchronous, Non-blocking in JavaScript and how to handle them with Promises or Async/await (my personal choice).
Related
I'm learning AWS so go easy on me if this is an obvious issue. My goal is to use the Cognito Authorizer in API Gateway to authenticate my users. That part works fine. I can get tokens to the front end and back to the api. What I'm trying to do now is pull some user attributes from the user pool so I can put them in some denormalized fields in Dynamo. It seems the best way to do that is to pull the user attributes on post (if there's a better way please feel free to answer with that as well), which requires cognitoidentityserviceprovider.listUsers. The problem is the callback never executes. At first I was getting not authorized errors, so I updated the IAM policy, and that went away, now I get the logs outside the callback, but not inside it.
Snippet
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
let sub = event.requestContext.authorizer.claims.sub;
let userpool = event.stageVariables.userpool;
const cogParams = {
UserPoolId: userpool,
Filter: "sub=\""+ sub + "\"",
Limit: 1
};
console.log('listing',JSON.stringify(cogParams));
cognitoidentityserviceprovider.listUsers(cogParams, function(err, data) {
console.info('entered callback');
if (err) {
console.log(err, err.stack); }
else {
console.log('data',data);
}
console.info('end callback');
});
console.log('listed');
Expected output
listing {...}
entered callback
data {...}
end callback
listed
Actual Output
listing {...}
listed
From the await usage, it seems you are using an async handler. Also, according to AWS documentation on using async/await, it's noted that functions that take a callback do not return a promise, you must chain them with the .promise() method:
Most functions that take a callback do not return a promise. Since you only use await functions that return a promise, to use the async/await pattern you need to chain the .promise() method to the end of your call, and remove the callback.
So, in order for the await to properly wait for the listUsers to execute instead of moving forward, you should update your code to:
Async handler version
const AWS = require('aws-sdk');
exports.handler = async (event) => { // <-- async handler
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
let sub = event.requestContext.authorizer.claims.sub;
let userpool = event.stageVariables.userpool;
const cogParams = {
UserPoolId: userpool,
Filter: "sub=\"" + sub + "\"",
Limit: 1
};
console.log('listing', JSON.stringify(cogParams));
// use `await` due to async handler
let userList = await cognitoidentityserviceprovider.listUsers(cogParams, function(err, data) {
console.info('entered callback');
if (err) {
console.log(err, err.stack);
}
else {
console.log('data', data);
}
console.info('end callback');
}).promise(); // <-- added the .promise() for the `await`
console.log('listed', userList); // <-- got the result also on the variable
};
Note that the callback was kept to show it executing but, as per AWS documentation suggestion, it can be removed.
If you prefer the callback programming model:
Non-async handler version
const AWS = require('aws-sdk');
exports.handler = (event) => { // <-- no `async` keyword
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
let sub = event.requestContext.authorizer.claims.sub;
let userpool = event.stageVariables.userpool;
const cogParams = {
UserPoolId: userpool,
Filter: "sub=\"" + sub + "\"",
Limit: 1
};
console.log('listing', JSON.stringify(cogParams));
cognitoidentityserviceprovider.listUsers(cogParams, function(err, data) {
console.info('entered callback');
if (err) {
console.log(err, err.stack);
}
else {
console.log('data', data);
}
console.info('end callback');
});
console.log('listed');
};
On using the cognitoidentityserviceprovider.listUsers method, it might be a bit more correct to use cognitoidentityserviceprovider.adminGetUser since you should also have access to the username and you're always retrieving a single user.
Trying to delete a doc Mongoose using findByIdAndDelete but im always getting back a null when I know the doc id am passing to the function exists in MongoDB
I've tried:
findByIdAndRemove
findByIdAndDelete
findOneAndDelete -> this with the code below deletes two docs rather than one!
async function deleteCourse(id)
{
const result = await Course.findByIdAndDelete(id,(err, res) => {
if (err){
console.log(err)
}
else{
console.log("Deleted course: ", res);
}
})
}
deleteCourse('5a68fdf95db93f6477053ddd')
As #Yousaf suggested, you dont need the callback function if you are using async/await function. Also use try/catch to have the same behavior.
async function deleteCourse(id)
{
try {
const result = await Course.findByIdAndDelete(id);
console.log("Deleted course: ", result);
} catch(err) {
console.log(err)
}
}
deleteCourse('5a68fdf95db93f6477053ddd')
I want to send an order-confirmation email(with nodemailer) to customers when they finished the order.
In this confirmation email I want to insert some data from the buyed products.
I've used many async/await combinations but none of them succeeded.
The code I got for now:
async function getProductData(products) {
let productsArray = products;
try {
let productsHtml = "";
for await (const product of productsArray) { // loop through the products
db.collection("collectionname").findOne(query, (err, data) => { // get data for every product
if (err) console.log(err)
else {
let productHtml = `<p>productname: ${data.productname}</p>
<img src="${data.imageSrc}">`;
productsHtml += productHtml; // add the product html string to productsHtml
}
})
}
} catch (error) {
console.log(error);
}
return productsHtml; //return the productsHtml string. But it returns "", because it doesn't await the mongodb function
}
async function sendConfirmationMail() {
let productsHtml = await getProductData(products); //"products" is an array
//nodemailer code
let transporter = nodemailer.createTransport({
// configuration
});
let email = await transporter.sendMail({
//email information
html: `<p>Thank you for your order!</p>
${productsHtml}`;, // insert the html string for all products into the mail
});
}
The problem is that the return statement in getProductData() fires before the for loop finishes. And then returns undefined.
Does anyone have tips on how I can implement async/await the correct way here?
You are not awaiting your database call. You are passing it a callback, so it does not return a Promise :
.findOne(query, (err, data) => {
This has a callback. You are passing it a function as second argument, that takes (err,data) as its own arguments. You need to remove this callback and use the async/await syntax with try/catch. Something like
const data = await db.collection("collectionname").findOne(query);
On the other hand you don't need to for await (const product of productsArray). You are iterating an array of static values, not an array of Promises. Remove this await.
So I have a post route which is an async await function, and I have a reuest inside it which pulls some data from the api and I want to save the content of that body in a variable outside the request function
I have tried using promises but I am not really familiar with that.
//#route POST api/portfolio/stock
//#desc Create or update a user's stock portfolio
//#access private
router.post(
"/stock",
auth,
[
check("symbol", "symbol is require or incorrect.")
.not()
.isEmpty(),
check("qty", "Quantity of the stock purchased is required")
.not()
.isEmpty()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty) {
return res.status(400).json({ errors: errors.array() });
}
const { stock, qty } = req.body;
const newPortfolio = {};
newPortfolio.user = req.user.id;
newPortfolio.stocks = [];
if(stock) newPortfolio.stocks.stock = stock;
if(qty) newPortfolio.stocks.qty = qty;
request(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${config.get(
"API_KEY")}`, (error, response, body) => {
if (error) console.error(error);
if (response.statusCode !== 200) {
res.status(404).json({msg: 'No stock found'});
}
let content = JSON.parse(body);
let quote = content['Global Quote'];
});
newPortfolio.stocks.stockInfo = quote;
try {
let portfolio = await Portfolio.findOne({ user: user.req.id });
//update if exists
if(portfolio) {
portfolio = await Portfolio.findOneAndUpdate(
{ user: user.req.id },
{ $push: { stocks: newPortfolio.stocks }}
);
return res.json(portfolio);
}
//create if not found
portfolio = new Portfolio(newPortfolio);
await portfolio.save();
res.json(portfolio);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
I want to save myPortfolio.stocks.stockInfo using body of that request.
How to access the body of a request outside in an async function?
You don't. Asynchronous results are only available in the asynchronous context. For an asynchronous function that returns the result via a callback, that means you consume/use the results INSIDE the callback. Any code that needs access to those results goes in the callback. In traditional async Javascript programming, you just continue the code of your function inside the callback.
Fortunately, promises and the invention of async and await can make the coding a bit simpler. For asynchronous functions that return a promise (instead of taking a callback), you can use await to get their result and you can write code that looks more like the sequential model, even though it's still asynchronous.
For example, this is what a rewrite of your function might look like where we switch the request-promise library (same as the request library, but returns a promise instead of uses a callback) and then we use await on the result:
const rp = require('request-promise');
router.post(
"/stock",
auth,
[
check("symbol", "symbol is require or incorrect.")
.not()
.isEmpty(),
check("qty", "Quantity of the stock purchased is required")
.not()
.isEmpty()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty) {
return res.status(400).json({
errors: errors.array()
});
}
const {
stock,
qty
} = req.body;
const newPortfolio = {};
newPortfolio.user = req.user.id;
newPortfolio.stocks = [];
if (stock) newPortfolio.stocks.stock = stock;
if (qty) newPortfolio.stocks.qty = qty;
try {
let body = await rp(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${stock}&apikey=${config.get("API_KEY")}`);
let content = JSON.parse(body);
newPortfolio.stocks.stockInfo = content['Global Quote'];
} catch (e) {
console.error(e);
res.status(404).json({
msg: 'No stock found'
});
return;
}
try {
let portfolio = await Portfolio.findOne({
user: user.req.id
});
//update if exists
if (portfolio) {
portfolio = await Portfolio.findOneAndUpdate({
user: user.req.id
}, {
$push: {
stocks: newPortfolio.stocks
}
});
return res.json(portfolio);
}
//create if not found
portfolio = new Portfolio(newPortfolio);
await portfolio.save();
res.json(portfolio);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
Note: your adding properties to newPortfolio.stocks which is an array is quite unusual. While it's not technically wrong, you would usually declare newPortfolio.stocks to be an object, not an array if you were just going to add properties to it and not use it like an actual array. It can often be confusing to people reading your code if you use the same variable as both an object and an array. Usually, you want a variable (or property) to behave as one or the other, not both.
I'm using node and express to create server for my app. This is how my code looks like:
async function _prepareDetails(activityId, dealId) {
var offerInfo;
var details = [];
client.connect(function(err) {
assert.equal(null, err);
console.log("Connected correctly to server");
const db = client.db(dbName);
const offers_collection = db.collection('collection_name');
await offers_collection.aggregate([
{ "$match": { 'id': Id} },
]).toArray(function(err, docs) {
assert.equal(err, null);
console.log("Found the following records");
details = docs;
});
})
return details;
}
app.post('/getDetails',(request,response)=>{
var Id = request.body.Id;
var activityId = request.body.activityId;
_prepareDetails(activityId,Id).then(xx => console.log(xx));
response.send('xx');
})
While calling getDetails API I am getting
await is only valid in async function error (At line await offers_collection.aggregate)
I am also getting red underline while declaring async function. Node version I'm using is 11.x. I'm also using firebase API. What am I doing wrong here?
You are missing the async declaration on one of your functions. Here is the working code:
async function _prepareDetails(activityId, dealId) {
var offerInfo;
var details = [];
client.connect(async function(err) {
assert.equal(null, err);
console.log("Connected correctly to server");
const db = client.db(dbName);
const offers_collection = db.collection('collection_name');
await offers_collection.aggregate([
{ "$match": { 'id': Id} },
]).toArray(function(err, docs) {
assert.equal(err, null);
console.log("Found the following records");
details = docs;
});
})
return details;
}
app.post('/getDetails', async (request,response)=>{
var Id = request.body.Id;
var activityId = request.body.activityId;
let xx = await _prepareDetails(activityId,Id);
response.send('xx');
})
Await can only be used in an async function, because await is by definition asynchronous, and therefore must either use the callback or promise paradigm. By declaring a function as async, you are telling JavaScript to wrap your response in a promise. Your issue was on the following line:
client.connect(function(err) {
This is where I added the async as mentioned before.
client.connect(async function(err) {
You will notice I made your route use async also, because you would've had an issue before. Notice the two lines in your original code:
_prepareDetails(activityId,Id).then(xx => console.log(xx));
response.send('xx');
Your response would send before you even made your database call because you are not wrapping the response.send within the .then. You could move the response.send into the .then, but if you are going to use async/await, I would use it all the way. So your new route would look like:
app.post('/getDetails', async (request,response)=>{
var Id = request.body.Id;
var activityId = request.body.activityId;
let xx = await _prepareDetails(activityId,Id);
response.send('xx');
})