await is only valid in async function - nodejs - javascript

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');
})

Related

Why am I still getting MongoExpiredSessionError even though I'm parsing the json in my azure function?

With regards to my Azure function, I cannot for the life of me figure out why I'm getting MongoExpiredSessionError: Cannot use a session that has ended error when trying to do an extremely simple insertion. I've tried JSON.parse(result); and JSON.stringify(result); but still getting the same error.
I've also tried removing the finally block, tried adding await in front of collection() but still getting the same error. I've pretty much done all the I can that I've found on the web to troubleshoot but to no avail.
What am I doing wrong?
Here's my code:
const mongo = require('mongodb');
const MongoClient = require('mongodb').MongoClient;
const uri = "myUri"
module.exports = async function (context, req) {
const client = await MongoClient.connect(uri, {useNewUrlParser:true}).catch(err => {context.log(err); });
let result = null;
let callerEntityProfile = req.body.CallerEntityProfile;
let packIdInput = callerEntityProfile.Entity.PackIdInput;
let userId = callerEntityProfile.Entity.UserID;
try {
const db = await client.db("myDB");
let collection = await db.collection("myCollection");
let query = { userId : userId };
result = await collection.findOne(query);
let insertPack = {
"PackId" : packIdInput
};
result.packsOwned.push(insertPack);
JSON.parse(result);
collection.insertOne(result, function(err, r) {
console.log('inside insertOne')
console.log(err); // this is where it's showing the error message
});
}
catch (err) {
context.log(err);
}
finally {
client.close();
}
}
Since it does not wait until the end of the asynchronous function, the close action is faster than insertMany action. You can try the following:
const DB = client.db(databasename);
const myAsyncFunction = async() => {
const collection = await DB.collection(nameofcollection); // do this INSIDE async function
await collection.insertMany(details); // similarly inside async
};
client.close();
Alternatively, you may try something like this:
client.connect(err => {
if(err) return console.log(err);
client.db(databasename).collection(nameofcollection)
.then(collection => collection.insertMany(details))
.then(() => client.close());
});
You can refer to these similar SO threads -
How to fix MongoError: Cannot use a session that has ended
MongoError: Cannot use a session that has ended
Node js throws MongoError
MongoError

Why isn't my async function waiting for the promise to be fulfilled

I am using ldapjs to query users from an ldap server.
If I put all the code just in a single script without using functions, the query works and I get the results I need.
I am now trying to use expressjs to serve a rest endpoint to enable querying of the ldap server, so I moved the ldapjs client.search code into a async function with a promise surrounding the actual search code.
After the promise code, I have a line which exercises the promise using await and stores the results of the promise in a variable. I then return that variable to the calling function which will eventually send the results back as a json-formatted string to the requesting browser.
The problem I am seeing is that the console.log() of the returned results is undefined and appears before the console.log statements inside the promise code. So it looks like the async function is returning before the promise is fulfilled, but I don't see why because in all the examples of promises and async/await I have seen this scenario works correctly.
Below is a sample script without the expressjs part to just make sure everything works correctly.
// script constants:
const ldap = require('ldapjs');
const assert = require('assert');
const ldapServer = "ldap.example.com";
const adSuffix = "dc=example,dc=com"; // test.com
const client = getClient();
const fullName = "*doe*";
var opts = {
scope: "sub",
filter: `(cn=${fullName})`,
attributes: ["displayName", "mail", "title", "manager"]
};
console.log("performing the search");
let ldapUsers = doSearch(client, opts);
console.log("Final Results: " + ldapUsers);
function getClient() {
// Setup the connection to the ldap server
...
return client;
}
async function doSearch(client, searchOptions) {
console.log("Inside doSearch()");
let promise = new Promise((resolve, reject) => {
users = '{"users": [';
client.search(adSuffix, searchOptions, (err, res) => {
if (err) {
console.log(err);
reject(err)
}
res.on('searchEntry', function(entry) {
console.log("Entry: " + users.length);
if (users.length > 11) {
users = users + "," + JSON.stringify(entry.object);
} else {
users = users + JSON.stringify(entry.object);
}
});
res.on('error', function(err) {
console.error("Error: " + err.message);
reject(err)
});
res.on('end', function(result) {
console.log("end:");
client.unbind();
users = users + "]}";
resolve(users)
});
});
});
// resolve the promise:
let result = await promise;
console.log("After promise has resolved.");
console.log(result);
return result
}
The output from the console.log statements is as follows:
Setting up the ldap client.
ldap.createClient succeeded.
performing the search
Inside doSearch()
Final Results: [object Promise]
Entry: 11
end:
After promise has resolved.
{"users": [{"dn":"cn=john_doe"}]}
I did strip out the code which creates the ldapjs client and redacted the company name, but otherwise this is my code.
Any ideas on why the doSearch function is returning before the promise is fulfilled would be greatly appreciated.
As #danh mentioned in a comment, you're not awaiting the response from doSearch. Since doSearch is an async function it will always return a promise, and thus must be awaited.
As a quick and dirty way to do that you could wrap your call in an immediately invoked asynchronous function like so:
// ...
(async () => console.log(await doSearch(client, opts)))();
// ...
For more info you might check out the MDN docs on asynchronous functions
I think there are a few issues in the provided code snippet. As #danh pointed out you need to await the doSearch call. However you may have not done that already since you may not be using an environment with a top async. This likely means you'll want to wrap the call to doSearch in an async function and call that. Assuming you need to await for the search results.
// script constants:
const ldap = require('ldapjs');
const assert = require('assert');
const ldapServer = "ldap.example.com";
const adSuffix = "dc=example,dc=com"; // test.com
const client = getClient();
const fullName = "*doe*";
function getClient() {
// Setup the connection to the ldap server
...
return client;
}
async function doSearch(client, searchOptions) {
console.log("Inside doSearch()");
return new Promise((resolve, reject) => {
users = '{"users": [';
client.search(adSuffix, searchOptions, (err, res) => {
if (err) {
console.log(err);
reject(err)
}
res.on('searchEntry', function(entry) {
console.log("Entry: " + users.length);
if (users.length > 11) {
users = users + "," + JSON.stringify(entry.object);
} else {
users = users + JSON.stringify(entry.object);
}
});
res.on('error', function(err) {
console.error("Error: " + err.message);
reject(err)
});
res.on('end', function(result) {
console.log("end:");
client.unbind();
users = users + "]}";
console.log(result);
resolve(users)
});
});
});
}
const opts = {
scope: "sub",
filter: `(cn=${fullName})`,
attributes: ["displayName", "mail", "title", "manager"]
};
(async function runAsyncSearch () {
console.log("performing the search");
try {
const ldapUsers = await doSearch(client, opts); // Await the async results
console.log("After promise has resolved.");
console.log("Final Results: " + ldapUsers);
} catch (err) {
console.error(err.message);
}
})(); // Execute the function immediately after defining it.

SyntaxError: await is only valid in async function, When connecting to Mongo DB using Node JS

I am trying to access a mongo database using an async / await function in Javascript using the code provided below. When I run the code, the terminal returns the following error:
SyntaxError: await is only valid in async function
The error is confusing to me, because of my use of "async" for newFunction. I have tried changing the location of "async" and "await," but no combination that I have tried so far has yielded successful execution. Any insight would be very much appreciated.
var theNames;
var url = 'mongodb://localhost:27017/node-demo';
const newFunction = async () => {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("node-demo");
//Find the first document in the customers collection:
dbo.collection("users").find({}).toArray(function (err, result) {
if (err) throw err;
theNames = await result;
return theNames;
db.close();
});
});
}
newFunction();
console.log(`Here is a list of theNames: ${theNames}`);
There are significant changes in your code, Please try below :
For Mongoose :
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let theNames;
let url = 'mongodb://localhost:27017/node-demo';
const usersSchema = new Schema({
any: {}
}, {
strict: false
});
const Users = mongoose.model('users', usersSchema, 'users');
const newFunction = async () => {
let db = null;
try {
/** In real-time you'll split DB connection(into another file) away from DB calls */
await mongoose.connect(url, { useNewUrlParser: true });
db = mongoose.connection;
let dbResp = await Users.find({}).limit(1).lean() // Gets one document out of users collection. Using .lean() to convert MongoDB documents to raw Js objects for accessing further.
// let dbResp = await Users.find({}).lean(); - Will get all documents.
db.close();
return dbResp;
} catch (err) {
(db) && db.close();
console.log('Error at newFunction ::', err)
throw err;
}
}
newFunction().then(res => console.log('Printing at calling ::', res)).catch(err => console.log('Err at Calling ::', err));
For MongoDB driver :
const MongoClient = require('mongodb').MongoClient;
const newFunction = async function () {
// Connection URL
const url = 'mongodb://localhost:27017/node-demo';
let client;
try {
// Use connect method to connect to the Server
client = await MongoClient.connect(url);
const db = client.db(); // MongoDB would return client and you need to call DB on it.
let dbResp = await db.collection('users').find({}).toArray(); // As .find() would return a cursor you need to iterate over it to get an array of documents.
// let dbResp = await db.collection('users').find({}).limit(1).toArray(); - For one document
client.close();
return dbResp;
} catch (err) {
(client) && client.close();
console.log(err);
throw err
}
};
newFunction().then(res => console.log('Printing at calling ::', res)).catch(err => console.log('Err at Calling ::', err));
Often dev's get confused with the usage of async/await & they do mix-up async/await's with callback()'s. So check the issues or not needed parts of your code below :
SyntaxError: await is only valid in async function - Is because you can not use await outside of an async function.
At this line dbo.collection("users").find({}).toArray(function (err, result) { - It has to be async function since await is being used in it.
var theNames; // There is nothing wrong using var but you can start using let.
var url = 'mongodb://localhost:27017/node-demo';
const newFunction = async () => {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("node-demo"); // You don't need it as you're directly connecting to database named `node-demo` from your db url.
//Find the first document in the customers collection:
/** If you create a DB connection with mongoose you need to create schemas in order to make operations on DB.
Below syntax goes for Node.Js MongoDB driver. And you've a mix n match of async/await & callbacks. */
dbo.collection("users").find({}).toArray(function (err, result) { // Missing async keyword here is throwing error.
if (err) throw err;
theNames = await result;
return theNames;
db.close(); // close DB connection & then return from function
});
});
}
newFunction();
console.log(`Here is a list of theNames: ${theNames}`);
The error is correct, as the function is not async function. Make your callback function in toArray async.
Example
var theNames;
var url = 'mongodb://localhost:27017/node-demo';
const newFunction = async () => {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("node-demo");
//Find the first document in the customers collection:
dbo.collection("users").find({}).toArray( async function (err, result) {
if (err) throw err;
theNames = await result;
return theNames;
db.close();
});
});
}
newFunction();
console.log(`Here is a list of theNames: ${theNames}`);

How to access the body of a request outside in an async function?

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.

Javascript Async/Await not returning, more like Await_Forever

I started started a node environment based off express, my issue is more with Promise.
I've written a module that has an http call (axios) and i'm using async/await in order to receive the response and handle it, everything is working great in the module in terms of flow and i'm able to execute console.log()s, yet my returned value isn't coming back to index.js.
index.js
works great and my readFile() function works great.
const getDistance = require('./actions/getDistance');
app.post('/find-closest', async (req, res) =>{
try{
let address = req.body.address;
let data = await readFile('src/agents.json');
return res.json(getDistance(JSON.parse(data),address));
} catch (e) {
console.log('Error');
console.log(e);
return res.json(e);
}
});
getDistance.js
module.exports = function (agents, origins) {
let destinations = '&destinations=' + agents.map(agent => agent.address).join('|');
const getDistances = async () => {
try {
return await axios.get(url + origins + destinations + apiKey)
} catch (error) {
console.error(error)
}
};
const setDistancesResponse = async () => {
const distances = await getDistances()
console.log('test',distances.data); //<=this returns
return 'baboom'; //this is not returned through
};
setDistancesResponse();
};
I am expecting my endpoint to return a JSON response of the string "baboom".
What am I doing wrong here?
Thanks, Bud
Your exported getDistance function doesn't return anything. You are probably looking for
module.exports = async function(agents, origins) {
const destinations = '&destinations=' + agents.map(agent => agent.address).join('|');
const distances = await axios.get(url + origins + destinations + apiKey)
console.log('test',distances.data); // this logs something
return 'baboom';
};
and given that this now returns a promise, you will have to wait for the result value:
app.post('/find-closest', async (req, res) =>{
try{
let address = req.body.address;
let data = await readFile('src/agents.json');
return res.json(await getDistance(JSON.parse(data), address));
// ^^^^^
} catch (e) {
console.error('Error', e);
return res.json(e);
}
});
I think you misses return value. Try this:
return setDistancesResponse();

Categories

Resources