Firebase HTTP Cloud Functions - Read database once - javascript

I have a Firebase HTTPs function. The function needs to read a value from a Firebase database based on the query parameter, and return a result based on this data.
The Firebase JS SDK says to do this using:
return firebase.database().ref('/users/' + userId).once('value').then(function(snapshot) {
var username = snapshot.val().username;
// ...
});
However, the Cloud functions examples have:
var functions = require('firebase-functions');
functions.database.ref('/');
But the DB reference doesn't have the method once, only onWrite (https://firebase.google.com/docs/reference/functions/functions.database.RefBuilder). This is obviously for DB write functions, rather than HTTP functions.
Is there a correct way to read from the database once in a HTTP function? Can I use the normal Firebase SDK, or is there a better way?
Thanks.

I found the solution in combining the answer here on how to get the parameter and an answer from Michael Blight to
How to run query from inside of Cloud function?
The answer there also shows what is required to use firebase-admin.
The following works for me when calling my-project.firebaseapp.com/event/123/.
var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.showEvent = functions.https.onRequest((req, res) => {
const params = req.url.split("/");
const eventId = params[2];
return admin.database().ref('events/' + eventId).once('value', (snapshot) => {
var event = snapshot.val();
res.send(`
<!doctype html>
<html>
<head>
<title>${event.name}</title>
</head>
<body>
<h1>Title ${event. name} in ${event.city}</h1>
</body>
</html>`
);
});
});

You're confusing two parts:
the firebase-functions module, which contains the logic to trigger based on database calls with functions.database.ref('/path').onWrite().
the firebase-admin module, which allows your function to call into the database.
Since you have a HTTP function, you should trigger as the documentation for HTTP functions shows:
exports.data = functions.https.onRequest((req, res) => {
// ...
});
Then in your function, you access the database as the documentation for the Admin SDK shows:
return admin.database().ref('/users/' + userId).once('value').then(function(snapshot) {
var username = snapshot.val().username;
// ...
});
So in total:
exports.date = functions.https.onRequest((req, res) => {
admin.database().ref('/users/' + userId).once('value').then(function(snapshot) {
var username = snapshot.val().username;
res.status(200).send(username);
});
});
Note that this is a tricky pattern. The call to the database happens asynchronously and may take some time to complete. While waiting for that, the HTTP function may time out and be terminated by the Google Cloud Functions system. See this section of the documentation.
As a general rule I'd recommend using a Firebase Database SDK or its REST API to access the database and not rely on a HTTP function as middleware.

Related

Platform exception while using google cloud function

I am making an app in flutter and using cloud function in firebase to update a number in Firestone,
index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.getRandomNumbers=functions.https.onCall((context)=>
{
const numbers=admin.firestore().collection('RandomNumbers').document('CurrentRandomNumber');
return numbers.add({
RandomNumber=5;
});
});
and this is the function
onPressed: () async{
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'getRandomNumbers',
);
await callable.call().catchError((error){
print('$error');
});
},
The error that is coming is
PlatformException(functionsError, Cloud function failed with exception., {code: NOT_FOUND, details: null, message: NOT_FOUND})
I also found these in my log which I may think is the reason of this
W/DynamiteModule(12544): Local module descriptor class for providerinstaller not found.
I/DynamiteModule(12544): Considering local module providerinstaller:0 and remote module providerinstaller:0
W/ProviderInstaller(12544): Failed to load providerinstaller module: No acceptable module found. Local version is 0 and remote version is 0.
NOTE
I have updated my Google Play services to the latest.
I have taken permission of the INTERNET in the manifest file.
redownloaded the .json file and checked
The code in your Callable Cloud Function is not correct.
By doing
const numbers = admin.firestore().collection('RandomNumbers').document('CurrentRandomNumber');
you are declaring a DocumentReference. A DocumentReference does not have an add() method.
If you want to get the value of a specific field of this document you should use the asynchronous get() method, as follows:
exports.getRandomNumbers = functions.https.onCall((data, context) => {
const numbers = admin.firestore().collection('RandomNumbers').document('CurrentRandomNumber');
return numbers.get().then(documentSnapshot => {
if (documentSnapshot.exists) {
console.log('Document retrieved successfully.');
const aNumber = documentSnapshot.data().aNumber;
// Do something
// ...
return null; // or return the promise returned by an asynchronous method call,
// see https://firebase.google.com/docs/functions/terminate-functions?authuser=1
} else {
console.log('Document does not exist.');
return null;
}
})
});
If you want to increment the value of a specific field of the Firestore doc, you should probably use a Transaction (it depends on your exact use case).

node.js: firebase snapshot return null

I'm using firebase functions to run the code for my flutter app. That sends a push notification to my app. I want to get the token. But I'm getting null value at snap.val(). Here how my code looks like:
const functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp();
var db = admin.database();
exports.CreateTicket = functions.firestore
.document('tickets/{username}')
.onCreate((snapshot, context) => {
console.log(snapshot.data().clientUid);// this one working fine.
var ref = db.ref("token/BX3jXKVpOkRa80SIKb7jPwfbU0c2/");
ref.once("value", (snap) => {
console.log(snap.val()); // this one getting null,
});
});
Your code is querying Realtime Database, but the screenshot here is showing Firestore. These are completely different database systems. Your database query is simply finding nothing, because there is no data at the location of your query.
You will have to write your code using the Firestore APIs for nodejs to read the document you have shown here.

How to update or set property of firestore document after .onCreate cloud function trigger

I'm currently trying to integrate Stripe with my Firebase's Cloud Firestore db through using Cloud Functions for Firebase. The onCreate trigger is happening correctly but I also want it to update or set a specific field called "customer_id" into the right document in my Users collection. I think something is a little off about how I write my function since I'm not the most experienced with javascript.
I've also tried
return admin.firestore().ref(`Users/${user.uid}/customer_id`).set(customer.id);
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//const logging = require('#google-cloud/logging')();
const stripe = require('stripe')(functions.config().stripe.token);
const currency = functions.config().stripe.currency || 'USD';
// When a user is created, register them with Stripe
exports.createStripeCustomer = functions.auth.user().onCreate((user) => {
return stripe.customers.create({
email: user.email,
}).then((customer) => {
return admin.firestore().collection("Users").doc(user.uid).update({"customer_id": customer.id})
});
});
Customer gets created with Stripe no problem but the "customter_id" field is not getting updated on the Firestore db.
Print screen of the database:
Print screen of the error log:
As said in the comments, from the print screen of the error log, the code you have deployed does not correspond to the code you are referencing to in your question.
The code in your question looks correct.

Firestore + cloud functions: How to read from another document

I'm trying to write a Google cloud function that reads from another document. (Other document = not the document that triggered the cloud function.)
It's a bit of a treasure hunt to figure out how to do such a simple thing.
The cloud functions documentation seems to suggest to look at the admin SDK: "You can make Cloud Firestore changes via the DeltaDocumentSnapshot interface or via the Admin SDK."
https://firebase.google.com/docs/functions/firestore-events
The Admin SDK suggest to write the following line of code to get a client. But oh no, it's not going to explain the client. It's going to send us off to a wild goose chase elsewhere in the documentation.
var defaultFirestore = admin.firestore();
"The default Firestore client if no app is provided or the Firestore client associated with the provided app."
https://firebase.google.com/docs/reference/admin/node/admin.firestore
That link resolves to a general overview page with no direct clue on figuring out the next thing.
https://cloud.google.com/nodejs/docs/reference/firestore/0.10.x/
Digging a big around, there is a promising class called FireStoreClient. It has a 'getDocument' method that seems promising. The parameter seems complicated. Rather than simply passing the path into the method, it seems to want an entire document/collection something as a parameter.
https://cloud.google.com/nodejs/docs/reference/firestore/0.10.x/FirestoreClient#getDocument
var formattedName = client.anyPathPath("[PROJECT]", "[DATABASE]", "[DOCUMENT]", "[ANY_PATH]");
client.getDocument({name: formattedName}).then(function(responses) {
var response = responses[0];
// doThingsWith(response)
})
So, I'm trying to combine all of this information into a Google cloud function that will read from another document.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.updateLikeCount4 = functions.firestore
.document('likes/{likeId}').onWrite((event) => {
return admin.firestore()
.getDocument('ruleSets/1234')
.then(function(responses) {
var response = responses[0];
console.log('Here is the other document: ' + response);
})
});
That approach fails with:
admin.firestore.getDocument is not a function
I've also tried. admin.firestore.document, admin.firestore.doc, admin.firestore.collection, and many more. None of them seem to be a function.
All I want is to read from another Firestore document in my Google cloud function.
PS: They said the documentation is your friend. This documentation is a nightmare that follows the principle of scatter all the clues into the four directions of the wind!
Thank you, #frank-van-puffelen.
This is the working solution:
exports.updateLikeCount = functions.firestore
.document('likes/{likeId}').onWrite((event) => {
return admin.firestore()
.collection('ruleSets')
.doc(1234)
.get()
.then(doc => {
console.log('Got rule: ' + doc.data().name);
});
});

Firebase HTTP Trigger Cloud Function to change users' values in realtime database

So I have written code which should change a certain value in the database. I would use cron-jobs to trigger it in every 24h, but there is something wrong with my code.
const functions = require('firebase-functions');
exports.resetPicksStatus = functions.https.onRequest((req, res) => {
.ref('/users/{userId}')
.onWrite(event => {
const status = event.data.val()
if (status.resetted) {
return true
}
console.log("Resetting status for " + event.paramas.userId)
status.resetted = true
status.picksDone = resetToNil(status.picksDone)
return event.data.ref.set(status)
})
})
function resetToNil(s) {
var resetValue = s
resetValue = resetValue.replace(/\b1\b/ig, "0")
return resetValue
}
It looks like you're trying to put a Realtime Database trigger inside your HTTP trigger, which won't have the outcome you're looking for. Instead of using a database trigger, use the Firebase Admin SDK to access the database from within the HTTP trigger.
In your code, add
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const ref = admin.database().ref();
And use ref to access the database.
Check out the Admin SDK documentation here.
Here are some samples of Cloud Functions, some of which show the Admin SDK.
Here's a video showing how to use the Admin SDK
Here's a video on timing Cloud Functions

Categories