I'm trying to build a simple REST API using Express.js, first time using it.
So I run the Express application generator using npx express-generator. Then, on /routes/users.js, I add a get method to retrieve data from an MSSQL database with the following code:
router.get('/api/v1/test', (req, res) => {
(async () => {
try {
let pool = await sql.connect(dbConfig);
let result1 = await pool.request().query('select * from andromedadb.dbo.test');
console.dir(result1);
} catch (err) {
console.log('err', err);
}
})();
});
I know the connection is successful because the console prints the following:
{
recordsets: [ [ [Object] ] ],
recordset: [ { id: 1, name: 'hello', age: 17 } ],
output: {},
rowsAffected: [ 1 ]
}
My issue is that the browser tab keeps loading and executing the get method over and over.
I also tried the code snippet on https://www.js-tutorials.com/nodejs-tutorial/simple-example-of-nodejs-express-with-mssql/#Listing_Nodejs_MSSQL_Using_Express, but the very same thing was happening.
Thanks in advance.
You need to send the response back to the request. I just modified your code, Look at the changes in the below code.
router.get('/api/v1/test', (req, res) => {
(async () => {
try {
let pool = await sql.connect(dbConfig);
let result1 = await pool.request().query('select * from andromedadb.dbo.test');
console.dir(result1);
res.status(200).send(result1);
} catch (err) {
res.status(401).send(err);
console.log('err', err);
}
})();
});
You need to send back some response with your res object or call the next() function to go into your next middleware, otherwise you are just leaving your request hanging, thus the infinite load. Add a res.send() or res.json() function to mark the end of your function.
router.get('/api/v1/test', (req, res) => {
(async () => {
try {
let pool = await sql.connect(dbConfig);
let result1 = await pool.request().query('select * from andromedadb.dbo.test');
console.dir(result1);
res.status(200).send("Received the response successfully");
} catch (err) {
console.log('err', err);
res.status(500).send("Something went wrong");
}
})();
});
How are you calling the route from the front end application?
As you are using an async function for your get method, remember to use 'await':
const response = await getRecordSet();
// do stuff with 'response'
Related
I referred this stack overflow solution but still this did not help me.
I have a find method that works perfectly inside a controller but it does not work inside a async function.
Below is the cron job where i am calling this fuction inside my server.js:
// this performs the job everyday at 10:00 am
cron.schedule("00 10 * * *",async function () {
await notifyBookDefaulties();
});
Below i my async function:
const notifyBookDefaulties = async () => {
console.log("Start");
const getList = async () => {
console.log("Fetching");
const books = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }});
console.log(books);
console.log("Fetched");
};
try {
getList();
console.log("completed");
} catch (error) {
console.log(error);
}
console.log("End");
};
After execution i see the following output in my console:
Start
Fetching
completed
End
and i get the same output if i do const books = await Book.find({});
and if i use await getList() then i get the following output in my console:
Start
Fetching
and if i put a try/catch around const books = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }}); there is no error generated nor any output.
and if i remove const books = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }}) then i see the foolowing in my console
Start
Fetching
Fetched
completed
End
Below is my controller that works correctly and fetches the desired documents:
const dueBooks = async (req, res) => {
const result = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }});
res.status(200).json(result[0]);
};
I tried to few things but it did work. Thanks in advance.
There appear to be two problems. One is you don't instantiate your MongoDB connection when you run your cron like your controller does.
In addition to that, you should be using the await on your getList since you need to wait for that promise inside the try / catch block.
I am trying to use the google-sheets api with express and don't have much experience with javascript. I'm attempting to use pass a json object from express to react, but it seems that whenever I finally send the object, it just renders as empty on the frontend?
I've tried using res.body/res.data, but the object doesn't seem to have either. I've also tried to put as many awaits as I can everywhere to make sure the object is loaded in before sending, but nothing seems to do the trick. If I use res.json or res.send with just the response object, I get a circular structure converting to JSON error. Here is the code I'm working with.
async function docShit() {
// Initialize the sheet - doc ID is the long id in the sheets URL
const doc = new GoogleSpreadsheet(
"--SPREADSHEET ID--"
);
// Initialize Auth - see https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication
await doc.useServiceAccountAuth({
// env var values are copied from service account credentials generated by google
// see "Authentication" section in docs for more info
client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY,
});
await doc.loadInfo(); // loads document properties and worksheets
const sheet = doc.sheetsByTitle[--WORKSHEET TITLE--];
const rows = await sheet.getRows(); // can pass in { limit, offset }
return rows;
}
app.get("/home", async (req, res) => {
try {
await docShit()
.then((response) => {
res.send(Promise.resolve(response)); //console log shows the object, but res.send just sends nothing??
})
.catch((err) => console.log(err));
} catch (err) {
console.error(err.message);
}
});
There is no res.send at all in your code. Also, you use await and .then together, but I consider them alternatives. Try the following:
app.get("/home", async (req, res, next) => {
try {
var response = await docShit();
console.log(response);
/* If response is circular, decide which parts of it you want to send.
The following is just an example. */
res.json(response.map(function(row) {
return {id: row.id, cells: row.cells.map(function(cell) {
return {id: cell.id, value: cell.value};
};
})};
} catch (err) {
console.error(err.message);
next(err);
}
});
Ok, full disclosure I am a hobby coder so I understand there to be gaps in my knowledge. However I've tried all sorts of solutions for this and have been unable to get a working answer.
DESIRED RESULT
I make a call to my Express server, it fetches data from an external API and renders once the data has been retrieved.
PROBLEM
I cannot seem to make Express wait no matter how I lay out the async/await pattern. Currently my code is as follows:
Express.js
app.get('/getInventory', async (req, res) => {
try {
let inventory = await api.getInventory(req.query.id)
console.log(inventory)
res.json(inventory)
}
catch(err) {
console.log(err)
}
})
My api.js is currently as such:
exports.getInventory = async function(id){
let data = await manager.getInventoryContents(id, 570, 2, true, (err, inventory) => {
if (err) {
console.log(err)
Promise.reject(err)
}
else {
console.log('Success')
Promise.resolve(inventory)
}
})
return data
}
In case you're wondering I have the console.log() actions just to try and see when something is happening.
WHAT I AM GETTING SO FAR
With just about every variation I am getting the Express.js inventory as undefined (similarly no data being sent to the client), however I AM receiving a Success message (or even the inventory itself) from api.js.
I am guessing the right syntax is obvious once I complete it but I cannot seem to get it to function properly. What am I missing?
Try this:
exports.getInventory = async function(id){
return new Promise((resolve, reject) => {
manager.getInventoryContents(id, 570, 2, true, (err, inventory) => {
if (err) {
console.log(err);
reject(err);
}
else {
console.log('Success');
resolve(inventory);
}
}
}
}
The code below is a mix of https://www.w3schools.com/nodejs/nodejs_mongodb_find.asp
and
https://stackoverflow.com/questions/49982058/how-to-call-an-async-function#:~:text=Putting%20the%20async%20keyword%20before,a%20promise%20to%20be%20resolved.
When you look at the console.log below the code, things seem to be running out of order. I thought by making the function async and using the .then I would avoid those issues.
I want the MongoDB data retrieval function separate from the app.get function.
No data is being returned to the get request. I guess the app.get code is falling through and ending before the function returns the value. What do I need to correct?
async function getLanguageTranslationData(fromLanguage, toLanguage) {
console.log("Started getLanguageTranslationData")
const databaseUrl = "mongodb://localhost:27017"
const databaseName = 'MyCompanyPOC'
mongoClient.connect(databaseUrl, function(err, conn) {
if (err) throw err;
const collectionName = "Phrases";
var dbo = conn.db(databaseName)
var query =
{ $and:
[ {masterLanguage: fromLanguage},
{localizedLanguage: toLanguage}
]
}
console.log("query=" + JSON.stringify(query));
console.log("about to retrieve data");
dbo.collection(collectionName).find(query).toArray(
function(err, result) {
if (err) throw err;
console.log("Back from mongoDB.find()")
console.log(JSON.stringify(result))
return result
conn.close()
})
})
}
app.get("/api/gettranslations/:fromLanguage/:toLanguage",
async function(req, res) {
console.log("Backend: /api/gettranslations method started: " +
" fromLanguage=" + req.params.fromLanguage +
" toLanguage=" + req.params.toLanguage)
getLanguageTranslationData(
req.params.fromLanguage,
req.params.toLanguage)
.then((arrayResult) => {
console.log("got arrayResult back from getLanguageTranslationData")
res.status(200).json(arrayResult)
console.log("Backend: End of /api/gettranslations process")
})
})
Node.JS Console output:
listening on port 3001
Backend: /api/gettranslations method started: fromLanguage=en-US toLanguage=es-MX
Started getLanguageTranslationData
(node:44572) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
got arrayResult back from getLanguageTranslationData
Backend: End of /api/gettranslations process
query={"$and":[{"masterLanguage":"en-US"},{"localizedLanguage":"es-MX"}]}
about to retrieve data
Back from mongoDB.find()
[{"_id":"5f403f7e5036d7bdb0adcd09","masterLanguage":"en-US","masterPhrase":"Customers","localizedLanguage":"es-MX","localizedPhrase":"Clientes"},{ etc...
The thing is getLanguageTranslationData should return a promise so that you can use it as a promise in your handler, but in your case, call to getLanguageTranslationData will return undefined as all the code inside this function will execute asynchronously due to nodejs non-blocking nature.
So what you can do is return promise from your getLanguageTranslationData function like this.
function getLanguageTranslationData(fromLanguage, toLanguage) {
const databaseUrl = "mongodb://localhost:27017"
const databaseName = 'MyCompanyPOC'
return new Promise((resolve, reject)=>{
mongoClient.connect(databaseUrl, function(err, conn) {
if (err) reject(err);
else{
const collectionName = "Phrases";
var dbo = conn.db(databaseName)
var query =
{ $and:
[ {masterLanguage: fromLanguage},
{localizedLanguage: toLanguage}
]
}
dbo.collection(collectionName).find(query).toArray(
function(err, result) {
if (err) reject(err);
else
resolve(result);
})
}
})
})
}
and then use await in your handler to use that promise returned
app.get("/api/gettranslations/:fromLanguage/:toLanguage",
async function(req, res) {
try{
let arrayResult = await getLanguageTranslationData(req.params.fromLanguage, req.params.toLanguage);
res.status(200).json(arrayResult)
}catch(err){
// return error
}
})
The above code will give you the gist of what you need to do, actual code may vary according to your needs.
You can refer async-await from here
I got it working in this way, based on this example: Node.js, Mongo find and return data
#Namar's answer is probably correct too, but I was testing this the same time he posted. As the StackOverflow question/answer above notes, the up-to-date versions of MongoClient have support for promises. That post also shows how to put in a separate module, which is something I will probably do later this week.
function getLanguageTranslationData(fromLanguage, toLanguage) {
console.log("Started getLanguageTranslationData")
const databaseUrl = "mongodb://localhost:27017"
const databaseName = 'ShedCompanyPOC'
return mongoClient.connect(databaseUrl)
.then(function(conn) {
var collectionName = "UploadedDataeFromExcel";
var dbo = conn.db(databaseName)
var query =
{ $and:
[ {masterLanguage: fromLanguage},
{localizedLanguage: toLanguage}
]
}
console.log("query=" + JSON.stringify(query));
console.log("about to retrieve data");
var collection = dbo.collection(collectionName)
return collection.find(query).toArray();
}).then(function(result) {
console.log("Back from mongoDB.find()")
console.log(JSON.stringify(result))
//conn.close()
return result
});
}
app.get("/api/gettranslations/:fromLanguage/:toLanguage",
async function(req, res) {
console.log("Backend: /api/gettranslations method started: " +
" fromLanguage=" + req.params.fromLanguage +
" toLanguage=" + req.params.toLanguage)
getLanguageTranslationData(
req.params.fromLanguage,
req.params.toLanguage)
.then(function(arrayResult) {
console.log("got arrayResult back from getLanguageTranslationData")
res.status(200).json(arrayResult)
console.log("Backend: End of /api/gettranslations process")
}, function(err) {
console.log("The promise was rejected", err, err.stack)
})
})
My Firestore data model looks like this
Organizations/{orgId}/cloudAccounts/{cloudAccountId}/resources/{resourceId}
I am running a simple query like this
let documents = await db.collection("Organizations")
.doc("orgIdabc")
.collection("cloudAccounts")
.doc("cloudAccountIdabc")
.collection("resources")
.get();
console.log("LENGTH: ", documents.docs.length);
Now the problem is that when I try to log the document length, it's always giving me a different length. I'll share the output as well.
LENGTH: 18
LENGTH: 19
LENGTH: 19
LENGTH: 6
LENGTH: 3
LENGTH: 19
LENGTH: 12
LENGTH: 19
LENGTH: 19
Now the actual length is 19, but as you can see I am getting different lengths.
I've no idea what's the problem here, any help would be really appreciated.
Updated
as per request I am adding the complete code snippet
Also I've come to know that this problem is only happening on Admin SDK and not the client SDK
const { db } = require("./firestore");
const functions = require("firebase-functions");
exports.runScanOnAllCloudAccounts = functions.https.onRequest((req, res) => {
runScanOnAllCA();
return res.status(200);
});
async function runScanOnAllCA() {
try {
for (let i = 0; i < 10; i++) {
let documents = await db.collection("Organizations")
.doc("orgIdabc")
.collection("cloudAccounts")
.doc("cloudAccountIdabc")
.collection("resources")
.get();
console.log("LENGTH: ", documents.docs.length);
}
} catch (err) {
console.log("err: ", err);
}
}
Update 02 ===========>
I've updated the code to use Promise() heavy approach, as recommended by some users and I still get different document lengths.
I feel that people are missing the point here, Promises are just a way to run asynchronous code, we can resolve this by using async await that was already being used in the previous version of the code.
Still the following code snippet doesn't solve the problem.
const { db } = require("./firestore");
const functions = require("firebase-functions");
exports.runScanOnAllCloudAccounts = functions.https.onRequest(
async (req, res) => {
runScanOnAllCA(res)
.then(resolve => {
console.log(resolve);
})
.catch(err => {
console.log(err);
});
// return res.status(200);
}
);
async function runScanOnAllCA(res) {
return new Promise(async (resolve, reject) => {
db.collection("Organizations")
.doc("sumair-hello-world_axtr8")
.collection("cloudAccounts")
.doc("4ZQgjt94pvEQTlvxSJ75")
.collection("resources")
.get()
.then(querySnapshot => {
resolve(querySnapshot.docs.length);
})
.catch(err => {
reject(err);
});
});
}
You should use the Admin SDK in order to interact with Firestore from a Cloud Function.
Secondly, it is known that using await in loops can lead to "erratic" results. See, for example, https://www.google.com/search?client=firefox-b-d&q=for+and+await
Finally, note that you are incorrectly calling your asynchronous runScanOnAllCA() function. You should either use then() or make your Cloud Function async and use await, see the code below.
You should adapt your CF as follows for using the Admin SDK:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.runScanOnAllCloudAccounts = functions.https.onRequest(async (req, res) => {
await runScanOnAllCA(); //<--- Note the await here and the async above, since you are calling an async function
res.status(200); //<--- No need to use return in an HTTPS Cloud Function, just terminate it with res.redirect(), res.send(), or res.end().
});
async function runScanOnAllCA() {
try {
const db = admin.firestore();
//The loop was removed. Use another way if needed, e.g. Promise.all() or the techniques presented in the links above.
let documents = await db.collection("Organizations")
.doc("orgIdabc")
.collection("cloudAccounts")
.doc("cloudAccountIdabc")
.collection("resources")
.get();
console.log("LENGTH: ", documents.docs.length);
} catch (err) {
console.log("err: ", err);
}
}