Sending firebase database data to client through cloud function - javascript

I am trying to send data from my firebase database back to my client in a cloud function. I want to return the entire json child object. Here is the code for my cloud function:
exports.addNumbers = functions.https.onCall((data, context) => {
admin.database().ref('store/ingredients').once('value', function(snapshot) {
return snapshot.val();
});
});
Here is my client code that is invoking this cloud function and accessing its data:
const addNumbers = firebase.functions().httpsCallable('addNumbers');
addNumbers().then(result => {
console.log(result.data + "in client");
});
But the data returned in the client is null and the cloud function is returning only object [Object] in the firebase logs. Please someone help me.

You're almost there. You just need to return something from the top-level code in your Cloud Function.
The easiest way is to use a then clause:
exports.addNumbers = functions.https.onCall((data, context) => {
return admin.database().ref('store/ingredients').once('value').then((snapshot) => {
return snapshot.val();
});
});
Now the value from the database "bubbles up" to the calling code, and is then returned out of your function as a promise.
On modern JavaScript versions, you can make this code easier to read by using async/await:
exports.addNumbers = functions.https.onCall(async (data, context) => {
let snapshot = await admin.database().ref('store/ingredients').once('value')
return snapshot.val();
});
This works exactly the same under the hood (it's really just syntactic sugar), but most developers find it much easier to read.

Related

Accessing Firestore Document data within JavaScript https on request function

I am working on a piece of my app where I need to make a call to a Firebase Function, which parses through Firestore data to return a dictionary that I will use to populate UI in Swift.
My function is declared as so:
exports.getUIBetData = functions.https.onRequest( (request, response) => {
This function takes in a userID as body parameter. Then, I need to hit firebase to get a specific document's data tied to this userId and perform some actions on it. I believe I am running into some issues with the async functionality behind getting data from a document, as I keep getting errors or simple promises that haven't been resolved. Here is my query:
const body = request.body;
const userId = body.data.userId;
const bettorIdDoc = admin.firestore()
.collection("user_dim").doc(userId).get().data();
I can confirm that "user_dim" is a valid collection, and the userId is a key to a document within it. However, I can't access the fields tied to this doc.
I was originally trying with just .data(), and realized from the official documentation that you need to do .get().data(), however this is async. How do I handle the async nature when I am attempting to do this within my main function (exports.getUIBetData = functions.https.onRequest( (request, response) => {)?
Error:
TypeError: admin.firestore(...).collection(...).doc(...).get(...).data is not a function
Loading data from Firestore (and pretty much any cloud API) is an asynchronous operation. You can see this by checking the return type of get(), which is Promise<DocumentSnapshot> and not just DocumentSnapshot.
This means you'll have to use then or await (if you're in an async context) to be able call data():
const bettorIdRef = admin.firestore()
.collection("user_dim").doc(userId)
ref.get().then((snapshot) => console.log(snapshot.data());

Firebase cloud functions: can't extract parameters when invoking a callable function

I'm using the Flutter SDK to call a cloud function. I add a parameter someField but on the cloud side, it cannot be retrieved and is always undefined. This is how I call the cloud function
static Future<void> doSomething() async {
await Firebase.initializeApp();
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'myFunction',
);
dynamic resp = await callable.call(
<String, dynamic>{
'someField': 'Hurray!',
},
);
}
The cloud function is written as follows
exports.myFunction = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const myParameter = req.body.someField; // is always Undefined
// also tried
// req.query.someField
// req.params.someField
doSomething(myParamter)
})
});
You're mixing up callable functions on the client app with HTTPS functions on the backend. Please review the documentation to understand the difference between them. If you want to use the Firebase SDK on the client to invoke a Cloud Functions, you should declare that using onCall instead of onRequest. When you write a callable function with onCall, you will have access to the input arguments via the first parameter delivered to the callback.
exports.myFunction = functions.https.onCall((data, context) => {
// data.someField should be populated from your client request
});

Firebase callable function to read real time database

Here I am trying to access the user's data from real time database by providing the UID. I have tried so many things but none worked. I have followed the documentation but no luck I am keep getting error -
Sending back results [promise]
Another example for writing the data which I have followed to create my logic but it didn't worked -
exports.userData = functions.https.onCall((data, context) => {
// verify Firebase Auth ID token
if (!context.auth) {
return { message: 'Authentication Required!', code: 401 };
}
const userId = data.text;
const ref = database.ref('/USERS/' + userId);
return ref.on('value', (snapshot) => {
console.log(snapshot); /* <--- I have tried with this and without this none worked*/
})
.then(snapshot => {
return {
data: snapshot
};
}).catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
});
The error I get on client side is -
service.ts:160 POST https://us-central1-gokuapp.cloudfunctions.net/userData 500
error.ts:66 Uncaught (in promise) Error: INTERNAL
at new YN (error.ts:66)
at XN (error.ts:175)
at rC.<anonymous> (service.ts:231)
at tslib.es6.js:100
at Object.next (tslib.es6.js:81)
at r (tslib.es6.js:71)
Edit: Before, I was correctly writing the code on my end but, I was either getting the error or null object based on the changes that I made during the discovery process. Anyone who had faced the same problem, just remember this... "cloud functions takes time to warm up to get fully functional", even though I am really thankful to #Frank van Puffelen and #oug Stevenson for their input. :) :) :)
Don't use on() in Cloud Functions, since that attaches a persistent listener to a query (and it doesn't return a promise). Use once() instead to query data a single time and get a promise the resolves with a snapshot. Also you should use snapshot.val() to get a plain JavaScript object with the contents of the snapshot.
return ref.once('value') // use once() here
.then(snapshot => {
return {
data: snapshot.val() // also use val() here to get a JS object
};
})
.catch((error) => {
throw new functions.https.HttpsError('unknown', error.message, error);
});
Callable Cloud Functions can return any JSON data. Your snapshot variable is a DataSnapshot object however, which contains a lot more than just JSON data.
You're probably looking to return the snapshot's value:
.then(snapshot => {
return {
data: snapshot.val()
};

google firebase cloud functions + firestore

I'm trying to get data from firebase firestore inside my firebase cloud functions, my index.js looks like this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.writeReport = functions.https.onRequest((request, response) => {
var fetchedData;
db.collection("mycollection").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
fetchedData = doc.id;
console.log("data" + `${doc.id} => ${doc.data()}`);
});
});
response.send("data " + fetchedData);
});
This code gives me an output of "data undefined" so I guess this means that the foreach statement doesn't get executed.
I am new to google cloud so I've been looking through the documentation and I can't fins anything which is related to my issue.
Thanks in advance,
Guy.
db.collection("mycollection").get() is asynchronous and returns immediately. The then callback function will get executed some time later, after the query completes. then() is also asynchronous. In fact anything that returns a promise is async.
This means that your call to response.send() is executing before the query completes. The function terminates as soon as the response is sent, so the query never completes. You need to wait for the query to complete before sending the response. The easiest thing to do is simply call response.send from inside the callback.
db.collection("mycollection").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
fetchedData = doc.id;
console.log("data" + `${doc.id} => ${doc.data()}`);
});
response.send("data " + fetchedData);
});
If you're new to JavaScript, I strong suggest spending time learning how promises work. There is a video series on learning how to use promises in Cloud Functions that you might find helpful.

Cloud Functions for Firebase: how to read data from Database synchronously?

I using Cloud Functions for Firebase to build HTTP endpoint. Inside this endpoint I am trying to read some data from Firebase Realtime Database synchronously by this method:
function getSomethingFromDbSynchronously (currency) {
return new Promise((resolve, reject) => {
var db = admin.database();
var ref = db.ref("someref");
ref.orderByChild("somechild").equalTo("something").once("value", function (snapshot) {
resolve(snapshot);
});
});
}
But it is doesn't works for me. My API route returns by the first return statement before this request to the DB ends.
What am I do wrong ?
The code looks fine: you're creating a new promise and returning that from getSomethingFromDbSynchronously(). But the code that calls getSomethingFromDbSynchronously() will then need to wait for the promise to resolve, with something like:
getSomethingFromDbSynchronously("currency").then(function(snapshot) {
console.log(snapshot.val());
});
There is no way to make this synchronous, although you could look into the new async and await keywords, which simply make the above read as if it happens synchronously.
Note, that your code is a bit longer than needed. Since once() already returns a promise, you might as well return that directly:
function getSomethingFromDbSynchronously (currency) {
var db = admin.database();
var ref = db.ref("someref");
return ref.orderByChild("somechild").equalTo("something").once("value");
}

Categories