firebase functions endpoint cold starts - javascript

Firebase REST endpoint. I am getting a lot of NULL returns especially at startup. Looking through other issues, I think it is a Coldstart. I believe the issue is that I am using callbacks which is returning before firebase has a chance to return a dataset. I read a comment about callabcks from #puf - frank-van-puffelen
sugesting a Cold Start. So I'm trying to re-write as a promise. This code works, usually, but still get the cold start NULL data sets. How would I do this as a promise?
var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//=================================================================================================
// KeysFromAccountGet01
//=================================================================================================
// This function is not working correctly because it often returns a NULL set. probably
// because I am using callbacks instead of promises, and the callback returns before firebase
// can return a query. Usually it works.
// But I am fairly sure that I should be using PROMICES so as to wait for the data to arrive.
// that said, I can not figure out how to do a promise. Everythign I have tried returns nothing.
// some sugestions on how to do promises for this would be appreciated.
//curl 'https://us-central1-test.cloudfunctions.net/KeysFromAccountGet01?account=dporras8'
//curl 'https://us-central1-test.cloudfunctions.net/KeysFromAccountGet01?account='
//firebase deploy --only functions:KeysFromAccountGet01
exports.KeysFromAccountGet01 = functions.https.onRequest((req, res) =>{
var arr =[];
arr.push("1====+++starting");
arr.push("acount = "+ req.query.account);
admin.database().ref('/newacctok/'+req.query.account+'/tok3/').on('value', function(snapshot){
snapshot.forEach(function(miniSnapShot){
var tt = miniSnapShot.val();
var json = ({
"key":miniSnapShot.key,
"account":req.query.account,
"uuid":tt.uuid,
"ts2":tt.ts2,
"token":tt.token
});
arr.push(json);
})
.then(res.status(200).send(arr));
});
//===================================

I'm not sure these changes will help with your Null returns. Note that I changed on(), which leaves the listener attached, to once(). Also, I've seen answers from Frank van Puffelen cautioning against performing asynchronous processing in HTTPS request functions. I'll try to find his answers/comments and add them.
exports.KeysFromAccountGet01 = functions.https.onRequest((req, res) => {
var arr =[];
arr.push("1====+++starting");
arr.push("acount = "+ req.query.account);
// note change from on() to once()
admin.database().ref('/newacctok/'+req.query.account+'/tok3/').once('value')
.then(snapshot => {
snapshot.forEach(miniSnapShot => {
var tt = miniSnapShot.val();
var json = ({
"key":miniSnapShot.key,
"account":req.query.account,
"uuid":tt.uuid,
"ts2":tt.ts2,
"token":tt.token
});
arr.push(json);
});
res.status(200).send(arr)
});
});

Related

Firebase cloud functions errors

Greetings of the day to everyone,
So I'm having a really hard time with Firebase cause there's just so many versions and things going on. Its extremely complicated. I wanted to achieve some functionality which is not available through the client modular web 9 version.
So I have been trying to use the Cloud functions to just get the list of all the collections in a document.
My cloud function looks like this so far -
const functions = require("firebase-functions");
const admin = require('firebase-admin');
const { object } = require("firebase-functions/v1/storage");
admin.initializeApp();
const db = admin.firestore();
exports.addMessages = functions.https.onCall(async (data, context) => {
// Grab the text parameter.
const original = data.text;
var test;
const writeResult = admin.firestore().collection('users').doc(original)
const collections = await writeResult.listCollections();
collections.forEach(collection => {
console.log('Found subcollection with id:', collection.id);
test = collection.id;
});
// Send back a message that we've successfully written the message
return { id: test }
});
I call this function on my front end simply like this --
const functions = getFunctions();
const addMessage = httpsCallable(functions, 'addMessages');
const Cloud = () => {
addMessage({ docPath: `users/${userProfile.uid}` })
.then(function (result) {
var collections = result.data.collections;
console.log(collections);
})
.catch(function (error) {
// Getting the Error details.
var code = error.code;
var message = error.message;
var details = error.details;
// ...
});
}
However, I get a 500 ERROR. I have checked the payload and everything, the data is being passed and the function is being called but there is some other kind of bug that I seem to be missing.
If anyone has any ideas please do let me know.
First off, you're calling addMessage with an Object parameter and then trying to access it's payload by the text field of data, which is undefined. You could either pass users/${userProfile.uid} as a string parameter or assign data.docPath to original.
Also, test will only contain the last collection id, since it's being overwritten forEach of the collections. So, may I suggest you make test a string array and push() each collection id to it?
Finally, on the callback of addMessage, there is no data field to access in result. In the case you decide to use an array, result will simply be the array you returned from the cloud function.
P.S. I'm not sure I see a reason why this wouldn't work with the V9 modular SDK.

React Native & Firebase RTDB - having trouble with returns

I'm using React Native with the Firebase SDK (version 9). I have a function that is supposed to check in the database to see if I have an access token stored and when it expires if so. I need the code to return true or false as a result. I think I'm getting tripped up because 'get' is asynchronous. Here's what I have currently:
export const checkToken = (useruid) => {
const db = getDatabase();
let outcome = "not set"
const userRef = ref(db,'/users/'+useruid);
get(userRef).then((snapshot) => {
console.log('snapshot is', snapshot)
let expire_time = snapshot.val().expire_time
if (expire_time > Date.now() + 180000) {
console.log('expire time is more than 3 min in the future')
outcome = true
return true
} else {
console.log('token is stale, snapshot is ',snapshot)
outcome = false
return false
}
} )
console.log('outcome is now', outcome)
}
The last console.log appears first (outcome is now not set), and if I call checkToken in another function (as console.log(checkToken(someUserID)), I get back that it is returning undefined. And then I get all the console logging with the successful snapshot retrieval.
I've read about promises, and async/await, but I'm having trouble translating that to the Firebase RTBD SDK functions.
Many thanks in advance for help/pointers!
If you just change it to:
export const checkToken = async function (useruid) {
const db = getDatabase();
let outcome = "not set"
const userRef = ref(db,'/users/'+useruid);
await get(userRef).then((snapshot) => {
.... insert rest of code
It should work as you expect.
You mention reading about promises and async/await, but you may want to read about the JS event loop, too. Basically JS doesn't wait for asynchronous actions before moving on to the rest of the code. So the way you have it structured now it kicks off the get(userRef) and then just moves on while it waits for that promise to return.

Google cloud function onCreate not writing to the Database

I have created a google function for firebase that when A new conversation is added the function attaches it to the Users table under a new collection for each user in the conversation but when the function gets triggered nothing happens in the database, So far I have console logged the values tp make sure they ware being set right and they ware I have also tried looking at the google function logs and there are no errors according to the logs the script ran with no errors
Here is the code for the function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
export const onConversationCreated = functions.firestore.document("Conversations/{conversationID}").onCreate((snapshot, context) => {
let data = snapshot.data();
let conversationID = context.params.conversationID;
if(data){
let members = data.members;
for(let index = 0; index < members.length; index++){
let currentUserID = members[index];
let remainingUserIDs = members.filter((u: string) => u !== currentUserID)
remainingUserIDs.forEach((m: string) => {
return admin.firestore().collection("Users").doc(m).get().then((_doc) => {
let userData = _doc.data();
if(userData){
return admin.firestore().collection("Users").doc(currentUserID).collection("Conversations").doc(m).create({
"conversationID": conversationID,
"image": userData.image,
"unseenCount": 1,
});
}
return null;
}).catch(() => {return null})
});
}
}
return null;
});
Can someone tell me if there is something wrong with my code or do I have to give functions permission to write to the cloud firestore database?
You are not dealing with promises correctly. Your function needs to return a promise that resolves with all of the asynchronous work is complete. Right now, it's just returning null, and not waiting for any work to complete.
All Firestore operations are asynchronous and return a promise. You will need to use these promises to build a single promise to return from the function. That means each time you query and write a document, that's generating another promise to handle.
Also, you should know that there is no method create() that you're trying to use to add a document. Perhaps you meant to use set() instead. The code will crash if it tries to execute create().

Trying to make an external api call then insert data in to mongo db and then render the resulting entry to the template

I am fairly new to web development and am having a difficult time getting this route right. I want the route to call out to API then insert the data from the api into my mongo db and then render that result to a handlebars template. Not getting the promises thing I guess. Code below:
The code runs the api call, gets back data and even inserts into db but will not render the template after(I originally had it rendering with the api response but I want to have an db id attached when it renders over to the template). I am sure it has something to do with the promises. I have tried with callbacks with no luck so tried with async/await functions and this doesn't seem to work either. Again I still have issues with multiple callbacks so I was trying something else.
The code runs the api call, gets back data and even inserts into db but will not render the template after(I originally had it rendering with the api response but I want to have an db id attached when it renders over to the template). I am sure it has something to do with the promises. I have tried with callbacks with no luck so tried with async/await functions and this doesn't seem to work either. Again I still have issues with multiple callbacks so I was trying something else.
async function getRecipeData(param) {
let res = await axios.get("https://api.edamam.com/search?q=" + param + "&app_id=0abb0580&app_key=bc931d03c51359082244df2fa414c487");
var dataArray = res.data.hits
return (dataArray);
}
async function insertSearchedRecipes(resArray) {
let response = await
Recipe.create({
name: resArray[i].recipe.label,
image: resArray[i].recipe.image,
url: resArray[i].recipe.url
});
return response;
};
router.get('/getRecipes/:ingredient', function (req, res) {
res.redirect("/");
var params = req.params.ingredient;
console.log(params);
let recipeFind = getRecipeData(params);
recipeFind.then(function (result) {
console.log(result[0]);
for (i = 0; i < result.length; i++) {
var recipeFindCreate = insertSearchedRecipes(result);
};
recipeFindCreate.then(function (results) {
console.log("HELLO")
// console.log(results);
res.render("recipeResults", {
data: results
});
});
});
});
Standard trap async / await. The values returned from the function defined as async ALWAYS you must obtain by await / then.
By calling recipeFindCreate you only wait for last one, rest are make async.
let _ = [];
for (i = 0; i < result.length; i++) {
_.push(insertSearchedRecipes(result));
};
let results = await Promise.all(_);
As you refactorize the code, make sure that each variable is declared in a function.
async function insertSearchedRecipes(resArray) {
let response = await
Recipe.create({
name: resArray.recipe.label, //NOT: `resArray[i]`
image: resArray.recipe.image,
url: resArray.recipe.url
});
return response;
};
//and:
var recipeFindCreate = insertSearchedRecipes(result[i]); // not `result`
Also, take a look at the bulk / bath insert theme - adding a data series made in 1 operation.
BTW: try don't use var (global scope). Better use let / const

Reading from firebase inside a cloud function

Can anyone help me determine why firebase isn't returning the value from a cloud function?
It seems like reading from on database change is simple, but when doing a http on request, firebase functions just hangs and finally times out.
exports.getTotalPrice = functions.https.onRequest((req, res) => {
var data = "";
req.on('data', function(chunk){ data += chunk})
req.on('end', function(){
req.rawBody = data;
req.jsonBody = JSON.parse(data);
res.end(next(req.jsonBody));
})
});
function next(json) {
var total = 0;
for(var i = 0; i < json.products.length; i++) {
//Get product by UPC
console.log(order.products[i].upc);
codeRef.orderByKey()
.equalTo(order.products[i].upc)
.once('value', function(snap) {
console.log(snap.val()); // returns `null`
return (snap.val()[0].msrp);
})
//Multiply the price of the product by the quantity
console.log(order.products[i].quantity);
//add it to the total price
}
}
You are running several asynchronous functions but you aren't informing your function when they are done. The function needs to return a promise to succeed here.
Note also that if the database call fails, it's going to do so silently here. So you should capture errors and report those as well.
Note also that you're storing your distributed JSON data as arrays, which you probably shouldn't.
Note also that you're using .orderByKey().equalTo() when you could just be using .child(upc).
So what you've essentially got here is a mess. You need to spend some time in the guide and samples--you're going to spend a lot of time thrashing like this if you don't.
For a starting point, reduce your code set to the simplest use case runs as expected:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.testDbOp = functions.https.onRequest((req, res) => {
return admin.database()
.ref('/foo')
.once('value')
.then(snap => res.send(JSON.stringify(snap.val()))
.catch(e => console.error(e));
});
Once you have that working, if you want to fetch several values asynchronously, you can do that with Promise.all(), something like this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.testDbOp = functions.https.onRequest((req, res) => {
const promises = [];
const output = {};
const jsonData = {....};
jsonData.products.forEach(product => {
promises.push( codeRef.child(product.upc).once('value')
.then(snap => output[product.upc] = snap.val()[0].msrp);
});
return Promise.all(promises).then(() => res.send(JSON.stringify(output));
});
It doesn't seem like you are calling the database at all. I only see functions.https.onRequest() which is a http trigger https://firebase.google.com/docs/functions/http-events.
If you wanted to call the database it would have to be something more like functions.database.ref('/').onWrite(event => {}) so that it references the database https://firebase.google.com/docs/functions/database-events.
onWrite refers to any kind of change at that point in the database, not just writing to the database. https://firebase.google.com/docs/functions/database-events#reading_the_previous_value

Categories

Resources