Constructing an array from db response using async await in node - node.js

I'm trying to construct an array of database responses using the async await syntax.
I understand that since async await is just a way of wrapping promises in nicer syntax, it should be easy to convert the promise-oriented instructions given here into that format.
Here's what I have:
const sqlite3 = require('sqlite3').verbose();
const express = require('express')
const bodyParser = require("body-parser")
async function getDB(dbname) {
let db = new sqlite3.Database(dbname)
return db;
}
app.get('/', async (req,res) => {
let db = await getDB(dbname)
let r = await db.serialize(() => {
let results = []
db.each(`SELECT * FROM MyTable LIMIT 3`, (err, row) => {
if (err) {
console.error(err.message);
}
let m = row.id;
console.log(m);
results.push(m)
});
return results
});
res.send(await r)
})
app.listen(port, ipaddr);
When I call this, the response is:
{"open":false,"filename":"dbname.db","mode":65542}
but I expect:
[1,2,3]
What's happening and how do I fix it?
In the console, I can see the records are being retrieved:
[
1,
2,
3
] # this is exactly the thing I want returned! Why can't I retrieve it?
So I know the database call actually works, but I can't seem to get them /into/ any object I can get out of the await.

Try the sqlite package, rather than the sqlite3 used in the demo. It has better support for async await.

Related

Can't push data from firebase to array by using foreach in Node.js

I tried to fetch data from firebase firestore, which I get what I wanted by console.log(). But when I tried to store what I get to array using push(). It gives me empty value in return. Here is my code:
app.post('/getMemberInfo', async (req, res) => {
var memberKeyList = req.body.member
var memRef = await db.collection("user")
let newArray = []
memberKeyList.forEach((row) => {
memRef.doc(row.MemberID).get().then((doc)=>{
newArray.push(doc.data())
console.log(doc.data())
})
})
res.send(newArray)
})
Here on console.log I got all data, but in newArray it returns empty array. What did I do wrong here? Any solution?
The problem is that each get() call is an asynchronous operation, and your res.send(newArray) runs before any of these calls complete (which you can see by adding some more logging and checking the order of the output).
The solution is to use Promise.all to wait for all the asynchronous operations to complete before sending the result back to the client:
app.post('/getMemberInfo', async (req, res) => {
var memberKeyList = req.body.member
var memRef = await db.collection("user")
let newArray = []
const docs = Promise.all(memberKeyList.map((row) => memRef.doc(row.MemberID).get()));
const newArray = docs.map((doc)=> doc.data());
res.send(newArray)
})

Async function returns pending promise node js

This is a very easy question but no google search results return the correct answer.
const express = require("express");
const app = express();
const cors = require("cors");
const pool = require("./db");
const poolec2 = require("./db");
require("./function")();
returnMappings = async function(connection){
try {
let mapping = await connection.query("SELECT ticker FROM mappings");
let results = await mapping.rows;
//console.log(results);
return results;
} catch (err) {
console.error(err.message);
}
};
const mappings = returnMappings(poolec2);
console.log(mappings);
What am I missing here that is not returning my data? When I unquote console.log(results); I can see the desired results in my terminal. I've tried various versions of using .then but have not had any success return results.then;, const mappings = returnMappings(poolec2).then;, console.log(mappings.then);. I've also tried returning my results outside of my try catch with no luck. I'm really stuck trying to understand how I go about returning a from an async function.
EDIT
The goal is to pass the results from the above async function to another function to check if the a user inputted value exists in that vector of mappings. So indexOf based on a user input, I then use if else to return true or false. With the final results being either true or false.
checkMappings = function(string,input){
stringArray = string;
value = stringArray.indexOf(input);
if(value > -1){
return false
}else{
return true
}
};
SOLUTION
returnMappings = async function(connection,input){
try {
const mapping = await connection.query("SELECT ticker FROM mappings_production");
const results = await mapping.rows;
//console.log(results);
return results;
} catch (err) {
console.error(err.message);
}
};
checkMappings = function(string,input){
let stringArray = JSON.stringify(string);
let value = stringArrayC1.indexOf(input);
function test(a) {
let check;
if(a > -1) {
return true
}else {
return false
}
};
console.log(test(value));
return test(value);
};
const resMappingCheck = returnMappings(poolec2).then((mappings) => checkMappings(mappings,"AAPL"));
console.log(resMappingCheck);
this worked for what I needed to do
As others have pointed out, await can only be used in an async function, but using .then() is functionally equivalent.
This syntax that should work for you:
returnMappings(poolec2).then((mappings) => console.log(mappings));
if you want to do something more elaborate / multi-line, you can use curly braces like so:
returnMappings(poolec2).then((mappings) => {
console.log(mappings)
});
UPDATE:
If you want to chain two functions together, then you'll need to start with the .then() pattern: you can declare the callback function in .then() to be asynchronous. At that point, you can start to use await like you're used to.
I'm not sure what relationship you're trying to create between returnMappings() and checkMappings(), but you can chain them together like this: (note the use of async on the first line to allow the use of await inside the callback.)
returnMappings('test').then(async (mapping) => {
const checkResult = await checkMappings(mapping)
console.log(`checkMapping result: ${checkResult}`)
}).catch((err) => console.log(`Error: ${err}`))
Try this:
const mappings = await returnMappings(poolec2);
That will work if you wrap the code inside an async function. Or you could do:
let mappings;
returnMappings(poolec2).then(res => {
mappings = res;
});

loop through documents in a collection to get fields

I have a collection called users in firebase firestore. Each document in the collection users is a user registered in the app. Each document has a field called token_ids. How can I loop through all the documents to get the values in the token_ids field. I am using firebase cloud functions to do so. Here is the code snippet I tried using but it did not work:
const functions = require('firebase-functions');
const admin = require ('firebase-admin');
admin.initializeApp();
//fetch all token ids of users
const tokenReference = admin.firestore().collection("users");
const tokenSnapshot = await tokenReference.get();
tokenSnapshot.forEach((doc) =>{
console.log("Token ids are:" + doc.data().token_id);
});
});
Took me a while but finally found the solution to it. Here it is. It is the first solution given by Dhruv Shah but slightly modified :
async function fetchAllTTokenIds() {
const tokenReference = admin.firestore().collection("users");
const tokenSnapshot = await tokenReference.get();
const results = [];
tokenSnapshot.forEach(doc => {
results.push(doc.data().token_id);
});
const tokenIds = await Promise.all(results);
return console.log("Here =>" +tokenIds);
}
Since Firestore operations are asynchronous, you should ideally wrap your code in an async-await block.
For example:
async function fetchAllTTokenIds() {
const tokenReference = admin.firestore().collection("users");
const tokenSnapshot = await tokenReference.get();
const results = [];
// Recommendation: use for-of loops, if you intend to execute asynchronous operations in a loop.
for(const doc of tokenSnapShot) {
results.push(doc.data().token_id);
}
const tokenIds = await Promise.all(results);
}
In this way all the tokenIds variable will be populated with an array of tokenIds.
Alternatively, you can also make all the asynchronous calls in parallel since they are independent of each other using Promise.all (Reference)
async function fetchAllTTokenIds() {
const tokenReference = admin.firestore().collection("users");
const tokenSnapshot = await tokenReference.get();
const tokenIds = await Promise.all(tokenSnapShot.map(doc => {
return doc.data()
.then(data => (data.token_id))
}))
In this case, the tokenIds variable will contain the array of all the tokenIds.
How the code snippet will be structured depends whether you're using the Firebase Admin SDK, be it as a script ran on your local machine or a httpsCallable being called by a client app. For the first case, it is written like this:
In your script file.js, after initialising app, write the following code.
exports.test_f = async function() {
try {
const tokenReference = admin.firestore().collection("users");
const tokenSnapshot = await tokenReference.get();
tokenSnapshot.forEach((doc) =>{
console.log("Token ids are:" + doc.data().token_id);
});
} catch (error) {
console.log(error);
}
}
exports.test_f();
Run this script on your command line using the command node file.js, which will print the provided output

Chaining async await calls in Node/Express with an external time limit

I'm building a Slackbot that makes a call to an Express app, which then needs to 1) fetch some other data from the Slack API, and 2) insert resulting data in my database. I think I have the flow right finally using async await, but the operation is timing out because the original call from the Slackbot needs to receive a response within some fixed time I can't control. It would be fine for my purposes to ping the bot with a response immediately, and then execute the rest of the logic asynchronously. But I'm wondering the best way to set this up.
My Express route looks like:
const express = require('express');
const router = express.Router();
const knex = require('../../db/knex.js');
const slack = require('../../services/slack_helpers');
// POST api/slack/foo
router.post('/foo', async (req, res) => {
let [body, images] = await slack.grab_context(req);
knex('texts')
.insert({ body: body,
image_ids: images })
.then(text => { res.send('worked!'); }) // This sends a response back to the original Slackbot call
.catch(err => { res.send(err); })
});
module.exports = router;
And then the slack_helpers module looks like:
const { WebClient } = require('#slack/web-api');
const Slack = new WebClient(process.env.SLACKBOT_TOKEN);
async function grab_context(req) {
try {
const context = await Slack.conversations.history({ // This is the part that takes too long
channel: req.body.channel_id,
latest: req.headers['X-Slack-Request-Timestamp'],
inclusive: true,
limit: 5
});
} catch (error) {
return [error.toString(), 'error'];
}
return await parse_context(context);
};
function parse_context(context) {
var body = [];
context.messages.forEach(message => {
body.push(message.text);
});
body = body.join(' \n');
return [body, ''];
}
module.exports = {
grab_context
};
I'm still getting my head around asynchronous programming, so I may be missing something obvious. I think basically something like res.send perhaps needs to come before the grab_context call? But again, not sure the best flow here.
Update
I've also tried this pattern in the API route, but still getting a timeout:
slack.grab_context(req).then((body, images) => {
knex ...
})
Your timeout may not be coming from where you think. From what I see, it is coming from grab_context. Consider the following simplified version of grab_context
async function grab_context_simple() {
try {
const context = { hello: 'world' }
} catch (error) {
return [error.toString(), 'error']
}
return context
}
grab_context_simple() /* => Promise {
<rejected> ReferenceError: context is not defined
...
} */
You are trying to return context outside of the try block where it was defined, so grab_context will reject with a ReferenceError. It's very likely that this error is being swallowed at the moment, so it would seem like it is timing out.
The fix is to move a single line in grab_context
async function grab_context(req) {
try {
const context = await Slack.conversations.history({
channel: req.body.channel_id,
latest: req.headers['X-Slack-Request-Timestamp'],
inclusive: true,
limit: 5
});
return await parse_context(context); // <- moved this
} catch (error) {
return [error.toString(), 'error'];
}
};
I'm wondering the best way to set this up.
You could add a higher level try/catch block to handle errors that arise from the /foo route. You could also improve readability by staying consistent between async/await and promise chains. Below is how you could use async/await with knex, as well as the aforementioned try/catch block
const express = require('express');
const router = express.Router();
const knex = require('../../db/knex.js');
const slack = require('../../services/slack_helpers');
const insertInto = table => payload => knex(table).insert(payload)
const onFooRequest = async (req, res) => {
try {
let [body, images] = await slack.grab_context(req);
const text = await insertInto('texts')({
body: body,
image_ids: images,
});
res.send('worked!');
} catch (err) {
res.send(err);
}
}
router.post('/foo', onFooRequest);
module.exports = router;

how to export array created in a promise

I'm trying to follow a project from mozilla developer local library tutorial
. It includes learning how to use nodejs, express, mongoose and mongoDB. I am swapping out mongoose and mongoDB with firestore and using firebase functions to eventually host with firebase hosting.
The project uses a model, view, controller structure. I really want to stick to this structure to help break everything down into manageable chunks.
The first problem I’m trying to resolve is to return an array that I created in a promise to be exported as a function that is called from booksController.js.
A concise view of the structure I have is:
index.js
const booksRouter = require('./routes/books');
app.use('/books', booksRouter);
routes/books.js
const books_controller = require('../controllers/booksController');
router.get('/', books_controller.index);
controllers/booksController.js
var Books = require('..models/books');
exports.index = function(req, res){
var books = Books.AllBooks();
res.render('books',{books: books});
};
models/books.js
var booksRef = db.collection(books);
var allBooks = booksRef.get()
.then(snapshot => {
var books = [];
Snapshot.forEach(doc => {
books.push({doc.data: doc.data()});
});
});
.catch(err =>{
console.log('Error getting documents', err);
});
exports.AllBooks = function(req, res){
return books;
}
I've tried wrapping the promise in a function and returning the array to export it but I get undefined when console logging in booksController.js.
Can someone please enlighten me and complete the puzzle of how to return the array so it can be exported.
What you're trying to do, can't be done using require, since require is synchronous.
Instead what you can do is export a function that will fetch the books asynchronously, and use that function in your controller.
models/books.js
const booksRef = db.collection(books);
async function getBooks() {
// You can implement a cache and fetch the books only once.
const snapshot = await booksRef.get()
const books = [];
snapshot.forEach(doc => {
books.push({
doc.data: doc.data()
});
});
return books;
}
module.exports = getBooks;
controllers/booksController.js
const getBooks = require('./../models/books');
exports.index = async function(req, res){
const books = await getBooks(); // Handle exception
res.render('books',{books: books});
};

Resources