I have a function which should query firebase db and return a result.
function verifyToken(token)
{
var androidId = 'xxxxx';
admin.database(dbDEV).ref('profiles').orderByChild('androidId').equalTo(androidId).on('value',(snapshot)=>{
console.log(snapshot.val());
return snapshot.val();
});
}
I am using firebase functions for this . so the result is getting logged in firebase logs but i am not getting and return value while executing the function.
Two things:
Use once() instead of on() to query data a single time. on() establishes a listener that listens forever, until you remove the listener.
Realtime Database queries are all asynchronous, meaning they return immediately, and the callback function you provide is invoked some time later with the results. You can't simply return the results from the callback in order to return those results from the enclosing function. If you want verifyToken to yield query results to the caller, you should return a promise that resolves with the data.
You can start by using promises. For example:
function verifyToken(token) {
return new Promise(resolve => {
var androidId = 'xxxxx';
admin.database(dbDEV).ref('profiles').orderByChild('androidId').equalTo(androidId).on('value',(snapshot)=>{
console.log(snapshot.val());
resolve(snapshot.val());
});
});
}
And when you need the result:
verifyToken(token).then(result => {
... do stuff
});
To improve on this, you can use an async function. For example:
async function foo() {
const result = await verifyToken(token);
console.log(result);
}
Related
I'm building a React Native app using Firebase. I use the following method (among others) in one of my components to get data from Firebase:
The problem is that sometimes the data is loaded as an empty dictionary (Object {}) and I can't switch to the pages that require authorization because all the information belonging to the user is not received. I don't know how to print
The on() method sets a listener that listens for data changes at the users db node. It is not an asynchronous method, therefore you don't need to use async in the encapsulating function.
if I correctly understand your question, you should use the get() method, which is asynchronous, as follows:
async function getData() {
try {
const snapshot = await firebase
.database()
.ref('users')
.get();
if (snapshot.exists()) {
// Check here the value of the snapshot with snapshot.val()
// and act accordingly if it returns (Object {})
} else {
// ....
}
} catch (error) {
// ...
}
}
I am writing the Firebase Could Functions with TypeScript and the following is a simple method to update a document.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
export const handleTestData = functions.firestore.document('test/{docID}').onCreate(async (snap, context) => {
const data = snap.data();
if (data) {
try {
await admin.firestore().doc('test1/' + context.params.docID + '/').update({duplicate : true});
} catch (error) {}
}
});
In this method, the promise is handled by async await and there is no return statement and it's working fine. Most of the examples/tutorials I have seen always have a return statement in each method.
Is there any impact/difference I don't return anything in Firebase Cloud Functions? If I should return something, can I return null?
Is return value important in Firebase Cloud Functions?
Yes, it is really key, in a Cloud Function which performs asynchronous processing (also known as "background functions") to return a JavaScript promise when all the asynchronous processing is complete, as explained in the documentation.
Doing so is important for two main reasons (excerpts from the doc):
You make sure that the Cloud Functions instance running your Cloud Function does not shut down before your function successfully reaches its terminating condition or state.
You can avoid excessive charges from Cloud Functions that run for too long or loop infinitely.
Why is your Cloud Function running correctly even if you don't return a Promise?
Normally your Cloud Function should be terminated before the asynchronous operations are completed, because you don't return a Promise and therefore indicate to the Cloud Functions platform that it can terminate the Cloud Functions instance running the Cloud Function.
But sometimes, the Cloud Functions platform does not terminate the Function immediately and the asynchronous operations can be completed. This is not at all guaranteed and totally out of your control.
Experience has shown that for short asynchronous operations this last case happens quite often and the developer thinks that everything is ok. But, all of sudden, one day, the Cloud Function does not work... and sometimes it does work: The developer is facing an "erratic" behaviour without any clear logic, making things very difficult to debug. You will find a lot of questions in Stack Overflow that illustrate this situation.
So concretely, in your case you can adapt your code like:
export const handleTestData = functions.firestore.document('test/{docID}').onCreate(async (snap, context) => {
const data = snap.data();
if (data) {
try {
// See the return below: we return the Promise returned by update()
return admin.firestore().doc('test1/' + context.params.docID + '/').update({duplicate : true});
} catch (error) {
return null; // <- See the return
}
} else {
return null; // <- See the return
}
});
or like
export const handleTestData = functions.firestore.document('test/{docID}').onCreate(async (snap, context) => {
const data = snap.data();
if (data) {
try {
await admin.firestore().doc('test1/' + context.params.docID + '/').update({duplicate : true});
return null; // <- See the return
} catch (error) {
return null; // <- See the return
}
} else {
return null; // <- See the return
}
});
Returning null (or true, or 1...) is valid since an async function always returns a Promise.
For an instance, let's suppose I am calling a function in initState which gets some documents from the Firebase collection. I am iterating on those documents using async forEach and need to perform an await operation to get some data from another collection from firebase and saving them in a list then returning after the forEach is finished. But the returned list is empty as the second await function completes after return statement. How do I handle this? I have a hard time understanding Asynchronous programming so please a detailed explanation will be highly appreciated.
The code here is just an example code showing an actual scenario.
Future getList() async {
//These are just example refs and in the actual refs it does print actual values
var collectionOneRef = Firestore.instance.collection('Collection1');
var collectionTwoRef = Firestore.instance.collection('Collection2');
List<Map<String, dynamic>> properties = [];
QuerySnapshot querySnapshot = await collectionOneRef
.getDocuments()
.then((query) {
query.documents.forEach((colOneDoc) async {
var colOneID = colOneDoc.documentID;
await collectionTwoRef.document(colOneID).get().then((colTwoDoc) {
Map<String, dynamic> someMap = {
"field1": colTwoDoc['field1'],
"field2": colTwoDoc['field2']
};
properties.add(someMap);
properties.sort((p1, p2) {
//sorting based on two properties
var r = p1["field1"].compareTo(p2["field1"]);
if (r != 0) return r;
return p1["field2"].compareTo(p2["field2"]);
});
print(properties); //this prints actual data in it
));
});
});
});
print(properties); //This prints a blank list as [] and obviously returns blank list
return properties;
}
And now when I call this function in an initState of a stateful Widget and display it somewhere, it display a blank list. BUT, after I "Hot Reload", then it displays. I want to get the data and display it without hot reloading it. Thanks in advance
Sounds like you need to await the whole function itself in the block of code where you are calling getList().
you are async'ing and awaiting inside the function, and that looks fine which is why you are getting results when hot reloading. But it might be the case that you're not actually awaiting the entire function itself in the code that is calling this. Please Check.
here is my code of http cloud function that reads some documents and then send response
res.set('Access-Control-Allow-Origin', '*');
var orderId;
var result = "";
var userId;
var promoCode;
var promoRef;
var userDocRef;
var promoCodeDoc;
//userId = req.body.userId;
//orderId = req.body.orderId;
promoCode = req.body.promoCode;
//userDocRef = db.collection("Users").doc()
promoRef = db.collection("PromoCodes").doc(promoCode);
var transaction = db.runTransaction(t => {
return t.get(promoRef)
.then(promoCodeDoc => {
if(promoCodeDoc.exists){
result = "OK";
res.json(result);
}else{
result = "Invalid Promocode!";
res.json(result);
}
//t.update(cityRef, {population: newPopulation});
return true;
});
}).then(result => {
console.log('Transaction success!');
return true;
}).catch(err => {
console.log('Transaction failure:', err);
});
return Promise.all(transaction());
But This is not sending the response because functions ends but Firestore Transaction is still runnning in background .
Any Solution to my problem ?
Promise.all() expects a single array of promises as its argument, but you're not giving it an array argument. Secondly, the transaction variable is a promise, not a function. You can't call () a promise.
So I think the correct code would be return Promise.all([transaction]). This being said, you only have one promise so you don't need Promise.all and can just return transaction.
Not sure if this will solve all your problems though. If you log into the firebase console, navigate to the functions section, there's a "Logs" tab that allows you to see debugging output from your function executions. It might help you track down all the problems. I imagine there are already console errors logged pointing out the fact that transaction() is not a function.
I'm writing my first Firebase Cloud Function in TypeScript. The function queries Firestore looking for a document that matches a parameter. The documentation says that when using a promise I need to "finish" the promise so that the function knows when it can complete. How do I do that? Here is my function.
BTW my function only returns the "not found" result right now. And it does work. It does send the response to my client app, but only after the function times out.
export const validateMemberPin = functions.https.onRequest((request, response) => {
console.log('pin: ' + request.query.pin);
const query = admin.firestore().collection('access').where('memberPin', '==', request.query.pin);
return query.get().then((snapshot) => {
if (snapshot.empty)
response.json({'result': 'false'});
});
});
Cloud functions have a NodeJS environment, so to end a function, you just need to add a return statement, in your case just add a return before response.json like so:
return query.get().then((snapshot) => {
if (snapshot.empty)
return response.json({'result': 'false'});
});
However, it would be better if you handle both cases:
return query.get().then((snapshot) => response.json({'result': !snapshot.empty});