How to read a value from Realtime Databse using Cloud Functions? - javascript

I have a Cloud Functions, which triggers if a certain value in my Realtime Database changes. After that I need to read an other value from the Database.
I searched the web and found one solution. It worked: The function triggered as soon as the value at /ID/temp_id changed, but it took additional 5 seconds to read the value at /ID/I.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getID = functions.database.ref("/ID/temp_id").onUpdate((change, context)=>{ //triggers the function when value "temp_id" changes
const tempID = change.after.val();
const i_snap = admin.database().ref("/ID/i").once("value", function(snapshot){ //read the value at "/ID/i" from the databse
const i = snapshot.val();
})
})
Is there any way to read the value at /ID/I faster?

In general, you cannot simply speed up a simple database write like that. Be aware that Cloud Functions have an unavoidable cold start time for the first invocation of your function on a new server instance.
I would actually not expect your function to work at all because you're not returning a promise that resolves when all the asynchronous work is complete in your function. You're obliged to do that so that your function terminates normally.

You have to trigger a function through an HTTP request by using functions.https. This allows invoke a synchronous function.
Use functions.https to create a function that handles HTTP events. The event handler for an HTTP function listens for the onRequest() event.
Used as arguments for onRequest(), the Request object gives you access to the properties of the HTTP request sent by the client, and the Response object gives you a way to send a response back to the client.
exports.date = functions.https.onRequest((req, res) => {
// ...
});
More details documentation: https://firebase.google.com/docs/functions/http-events
Take a look at the example below:
var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
//Call function via HTTP requests. This allows invoke a synchronous function
exports.showValue = functions.https.onRequest((req, res) => {
const params = req.url.split("/");
const tempId = params[2];
return admin.database().ref('ID/' + tempId).once('value', (snapshot) => {
var value = snapshot.val();
res.send(`
<!doctype html>
<html>
<head>
<title>${value.name}</title>
</head>
<body>
<h1>Title ${value. name}, id ${value.id}</h1>
</body>
</html>`
);
});
});

Related

Firebase: Calling Cloud Function From Cloud Function

I am running in to an issue with Firebase cloud functions. I have an onWrite cloud function that triggers a sequence of events. I have a path for requests that the onWrite cloud function is tied to. When that cloud function executes, it deletes the new request for the requests path and pushes the request in to a render path/que that will be used client side for rendering UI elements/data. Once the data has been written to the render path, I call a vanilla javascript function that is not tied to any cloud events. The vanilla javascript function is supposed to reach out to an external API and fetch some data to later be updated on the render object that was pushed in to the render path.
The problem is that the vanilla javascript function never executes. I have been looking all over the web to figure out why this happening but can't seem to figure out why. I am on the Flame plan so outbound api requests should be allowed to my knowledge. Here an example of my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const request = require('request');
admin.initializeApp();
exports.requestModule = functions.database.ref('/requests').onWrite((change, context) => {
// Create reference to database
let db = admin.database();
if (context && context.auth && context.auth.uid) {
const afterData = change.after.val();
let uid = context.auth.uid;
let cleanData = afterData[uid];
cleanData.status = "loading";
// Remove the requested module from the requests path
let cleansePath = db.ref('/requests/' + uid);
cleansePath.remove().then((snapshot) => {
return true;
}).catch((error) => {
console.log(error);
return false;
});
// Add requested module to the render path
let renderPath = db.ref('/render/' + uid);
renderPath.push(cleanData).then((snapshot) => {
let val = snapshot.val();
let key = snapshot.key;
// Trigger the get weather api call
getWeather(uid, key, val);
return true;
}).catch((error) => {
console.log(error);
return false;
});
}
});
// Fetches data from external api
function getWeather (uid, key, obj) {
console.log('Fetching weather!');
let db = admin.database();
request('https://api.someweathersite.net/forecast/', (error, response, body) => {
if (!error && Number(response.statusCode) === 200) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
obj.data = body;
obj.status = 'loaded';
// Set data from api response in render object to be shown client side
let render = db.ref('/render/' + uid + '/' + key );
render.set(obj).then(() => {
return true;
}).catch((error) => {
console.log(error)
return false;
});
}
});
}
The console.log message at the top of the "getWeather" function never executes. I don't think that the "getWeather" function is ever executing.
If I put the api call directly in the onWrite "requestModule" function, the api call will work. However, when it calls an external function it never gets called/works. I basically want to have the "requestModule" function handle all requests and plan to have a module dispatcher that handles which module function/api data should be fetched from. That's why I don't want to keep the api call in the "requestModule" function. Any idea of why this happening or how I can get this working?
getWeather is performing asynchronous work to fetch some data, but it's not returning a promise to indicate when that work is complete. In fact, none of the async work you're performing here is correctly using the promises returned by the various API calls. It's not sufficient to simply use then() on each promise.
You need to keep track of all of the async work, and return a single promise that resolves only after all the work is complete. Otherwise, Cloud Functions may terminate and clean up your function before the work is complete. (Note that it's not deterministic which work may or may not actually complete before forced termination, but the only way to ensure that all work completes is through that single promise you return.)
You may want to watch my tutorials on using promises in Cloud Functions to get a better handle on what you're required to do make your functions work correctly: https://firebase.google.com/docs/functions/video-series/

Firebase Cloud Function in Javascript. Remove() function not working

I have a Firebase cloud function. Everything works as expected within the helloWorld function except the line deedRef.limitToLast(1).remove(); I also tried to do .ref(/deeds/${deedID}).remove() is there a reason why I can't remove data from firebase within cloud functions? The output from the http request is "Error: could not handle the request".
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const deedRef = admin.database().ref('/deeds');
const oldDeedRef = admin.database().ref('/oldDeeds');
exports.helloWorld = functions.https.onRequest((req, res) => {
deedRef.limitToLast(1).once("value", (snapshot) => {
snapshot.forEach((deedSnapshot) =>{
let deedID = deedSnapshot.val().id;
let text = deedSnapshot.val().message;
oldDeedRef.push({
id: deedID,
message: text
})
})
})
deedRef.limitToLast(1).remove();
res.send("Congrats For running the function");
});
The problem has nothing to do with Cloud Functions.
deedRef.limitToLast(1) returns a Query type object. Query doesn't have a method called remove(). Therefore, your code will fail at runtime with a message to that effect.
If you want to delete some data from Realtime Database, you're going to need a Reference type object, which has a remove() method. This will remove everything at the location of the reference.

How to write a cloud function for firebase that will change data based on time

My firebase database structure looks like this:
-events
-uniqueEventId
-endTimeStamp: 1507949100
-active: true
-uniqueEventId2
-endTimeStamp: 1807949100
-active: true
-uniqueEventId3
-endTimeStamp: 1900949100
-active: true
How do I cloud function which can get all the events with timestamps before the current time and set their active to false.
I'm not sure how you want Cloud Functions to be triggered in this case. I'll assume you want a HTTP trigger, so that you can simply call it from the browser or a web hook.
That means you start with a basic HTTP-triggered function:
exports.updateStatus = functions.https.onRequest((req, res) => {
// ...
res.status(200).send("done");
});
Next up you'll need to access the Realtime Database within this function. To do that you'll use the Firebase Admin SDK, which gives you easy administrative access within your code:
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.updateStatus = functions.https.onRequest((req, res) => {
// ...
res.status(200).send("done");
});
Then we get to the actual code to change the data. This is standard database access code and has little to do with Cloud Functions. In this case you want to query for timestamp, loop over the results, and set the active property:
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.updateStatus = functions.https.onRequest((req, res) => {
let now = Date.now();
let query = admin.database().ref("events").orderByChild("timestamp").startAt(now);
query.once("value").then(function(snapshot) {
var promises = [];
snapshot.forEach(function(child) {
promises.push(child.ref.update({ active: false }));
})
Promise.all(promises).then(function() {
res.status(200).send("done");
});
});
});
That last code is a bit tricky, since it deals with many asynchronous write operations. Cloud Functions will terminate your function after your send the response to the client, so it's important that you only send a response back after all those asynchronous writes are done. I use a Promise.all() for that. For more information on this asynchronous nature, read the documentation, this blog post, and watch this video.
For more info, I'd recommend studying the documentation for the Firebase Admin SDK.

How to run query from inside of Cloud function?

I'd like to perform a query on my database once a cloud function on my Firebase app is called.
Let's say I have a certain trigger on the database, consider the example provided in the get started guide on Firebase.
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
console.log('Uppercasing', event.params.pushId, original);
const uppercase = original.toUpperCase();
// I'D LIKE TO PERFORM A QUERY HERE, JUST A SIMPLE RETRIEVE BASED ON THE ID PROVIDED
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return event.data.ref.parent.child('uppercase').set(uppercase);
});
Which modules should I import, if any?
How can I perform the query on the DB?
Thank you in advance for your answer!
You can use the Node.js Admin SDK for this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.makeUppercase = functions.database()
.ref('/messages/{pushId}/original')
.onWrite(event => {
return admin.database().ref('/other')
.orderByChild('id').equalTo(event.params.pushId)
.once('value').then(snapshot => {
// there, I queried!
});
});

Firebase cloud function always timeout

I'm exploring the firebase cloud functions and I'm trying to send a notifications with an http request.
The problem is that even if I manage to send the notification, the request always goes timeout.
Here's my script
/functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.friendRequestNotification = functions.https.onRequest((req, res) => {
const senderId = req.query.senderId;
const recipientId = req.query.recipientId;
const getRecipientPromise = admin.database().ref(`/players/${recipientId}`).once('value');
const getSenderPromise = admin.database().ref(`/players/${senderId}`).once('value');
return Promise.all([getRecipientPromise, getSenderPromise]).then(results => {
const recipient = results[0];
const sender = results[1];
const recipientToken = recipient.child("notificationsInfo/fcmToken").val();
const notificationAuthorization = recipient.child("notificationsInfo/wantsToReceiveNotifications").val();
const recipientBadge = recipient.child("notificationsInfo/badgeNumber").val();
const senderUsername = sender.child("username").val();
const payload = {
notification: {
title: `FriendRequest`,
body: `You have a new friend request from ${senderUsername}!`,
badge: (recipientBadge+1).toString()
}
};
if (notificationAuthorization) {
return admin.messaging().sendToDevice(recipientToken, payload).then(response => {
});
}
return admin.database().ref(`/players/${recipientId}/notificationsInfo/badgeNumber`).setValue(recipientBadge+1);
});
});
Plus It seems that the badgeNumber in never updated, is that related to the timeout issue?
HTTP-triggered Cloud Functions work just like Express apps -- you have a response object (res) that you need to use to send something when the request is done. In this case, it looks like you could do something like:
return Promise.all([
/* ... */
]).then(() => {
res.status(200).send('ok');
}).catch(err => {
console.log(err.stack);
res.status(500).send('error');
});
#Michael Bleigh answer is perfectly fine for this question, let me add more in this for the future users.
As per firebase documentation:-
Use these recommended approaches to manage the lifecycle of your
functions:
Resolve functions that perform asynchronous processing (also known as
"background functions") by returning a JavaScript promise.
Terminate HTTP functions with res.redirect(), res.send(), or res.end(). (The case in this question.)
Terminate a synchronous function with a return; statement.
Note
It's important to manage the lifecycle of a function to ensure that it resolves properly. By terminating functions correctly, you can avoid excessive charges from functions that run for too long or loop infinitely. Also, you can make sure that the Cloud Functions instance running your function does not shut down before your function successfully reaches its terminating condition or state.
You need a paid plan (Blaze, pay as you go) to access external APIs.
You might see below warning in firebase functions log if the billing account is not configured.
Billing account not configured. External network is not accessible and
quotas are severely limited. Configure billing account to remove these
restrictions
Check this link for more information.

Categories

Resources