Firebase: Writing Custom Callable Cloud Function vs Cloud Firestore Trigger Function - firebase

I learned all about Google Cloud Functions Triggers and I understood that they are good for doing some data preprocessing before write/update/delete happens in Cloud Firestore. This is true right?
However, instead of listening to changes in the database, I would like to write custom code in Cloud Functions and call it directly from the client SDK.
The reason I want to do this is because I don't want to have long complicated database integration logic in my client code. I just want to call a cloud function named createGame for instance and let the custom cloud function handle the nested calls to the Firestore, do sanity checks and return me with only the clean data that the client will be displaying.
I found out that there is a Flutter plugin for this called cloud_functions.
Also this documentation shows how to write custom callable functions.
We can write the custom callable Cloud Function as follows:
exports.createGame = functions.https.onCall((data, context) => {
// ...
return {
...
}
});
We can call the corresponding custom callable Cloud Function from Flutter client as follows:
final dynamic response = await CloudFunctions.instance.call(
functionName: 'createGame',
parameters: <String, dynamic>{
'message': 'hello world!',
'count': clientCount,
},
);
There are tons of Cloud Functions trigger examples/videos/tutorials. But very few of callable Cloud Functions. That's why I am a little skeptic about this subject.
Question:
My question is if this is good practice? Or should I do what this specific Cloud Function is doing from the client? Please answer while keeping pricing, speed, reliability and other factors in mind also:)
One last thing,
The function implementation uses https.onCall. Do you think https is slowing things down?

Related

gcp cloud function queue

I have written a GCP Cloud Function with a http trigger ("REST API") that invokes a Cloud Function with a Firebase Realtime Database trigger (onCreate).
The Cloud Function with a Firebase Realtime Database trigger performs REST calls to other services based on received data from the REST API.
I have noticed that the called services sometimes returns http 429 (too many calls) since my REST API does not have a limit to how many calls can be received.
The REST API has security measures in place to prevent unauthorised calls to invoke the Cloud Function with a Firebase Realtime Database trigger (onCreate). I do not wish to limit the amount of calls to my API, but rather place all requests in a queue and process them in sequence.
It is important that all calls are processed as promptly as possible. I do not wish to process transactions in 60 second intervals.
In my current solution all calls to the GCP HTTP http REST API immediately triggers the GCP Cloud Function via Firebase Realtime Database insert (onCreate event).
What I would like is to maybe have a queue in between my REST API and the Firebase Realtime Database insert (onCreate event) to ensure that only one GCP Cloud Function can execute simultaneously.
What is the best way to achieve this functionality?
Kind regards /K
EDIT:
Might Maximum instances be a solution here?
https://cloud.google.com/functions/docs/configuring/max-instances
You can Trigger Cloud Functions using Cloud Tasks. Here's an example I use whereby emails are placed inside of the Cloud Task queue I created and they are then processed by the task runner one-after-another:
import { CloudTasksClient } from '#google-cloud/tasks';
// define and use the following for your task:
// CLOUD_FUNCTION_URL
// TASK_HTTP_METHOD
// TASK_CONTENT_TYPE
// SERVICE_ACCOUNT_EMAIL
// PROJECT_ID
// REGION
// QUEUE_NAME
const client = new CloudTasksClient({ projectId: PROJECT_ID });
/**
* Build the Cloud Task
* In the below example we take a POST body and
* stringify it before converting to base64.
*/
const convertedPayload = JSON.stringify(payload);
const body = Buffer.from(convertedPayload).toString('base64');
const task: TaskT = {
httpRequest: {
url: CLOUD_FUNCTION_URL,
httpMethod: TASK_HTTP_METHOD,
body,
headers: {
'Content-Type': TASK_CONTENT_TYPE,
},
oidcToken: {
serviceAccountEmail: SERVICE_ACCOUNT_EMAIL,
},
},
scheduleTime: {
seconds: Date.now() / 1000, // <--- start the task now (in unix time)
},
};
return client.createTask({
parent: client.queuePath(PROJECT_ID, REGION, QUEUE_NAME),
task,
});
You'll also need to configure some IAM permissions for your development and app IAM users like Cloud Functions Invoker and Cloud Functions Viewer.

How do you return data from a back end node js to front end html?

Im working with Firebase functions. I want to return data from a Node JS cloud function to to be able to use it with html /front end code.
const functions = require("firebase-functions");
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
exports.helloWorld = functions.https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
Keep in mind that not all the Javscript code you have will be in a Cloud Function, code that directly interacts with your front end will still need to be in there, however, any code that directly operates with firebase can be transfered to a Cloud Functions.
You could use either Callable Functions, which can be called directly from your Javascript code but are hosted on the cloud, or HTTP Functions which are basically a HTTP request to an URL that sends you a response when it's done.
Cloud Functions also provides an emulator so you can test everything locally.
Before starting anything thought, I would recommend you to watch this playlist provided by Firebase that gives a really good intruction to Cloud Functions, so exactly what you are looking for.

How to continue running Firebase Cloud Function after request is finished

Really bizarre that Firebase doesn't seem to work quite like typical Express app. Whatever I write in Express and copy-paste to Firebase Functions I typically get error. There is one that I can't figure out on my own though.
This endpoint is designed to start a function and live long enough to finish even longer task. That request is a webhook (send docs, we will transform them and ping you when it's done to specified another webhook). Very simplified example below:
router.post('/', (req, res) => {
try {
generateZipWithDocuments(data) // on purpose it's not async so request can return freely
res.sendStatus(201)
} catch (error) {
res.send({ error })
}
})
On my local machine it works (both pure Express app and locally emulated Firebase Functions), but in the cloud it has problems and even though I put a cavalcade of console.log() I don't get much information. No error from Firebase.
If generateZipWithDocuments() is not asynchronous res.sendStatus() will be immediately executed after it, and the Cloud Function will be terminated (and the job done by generateZipWithDocuments() will not be completed). See the doc here for more details.
You have two possibilities:
You make it asynchronous and you wait its job is completed before sending the response. You would typically use async/await for that. Note that the maximum execution time for a Cloud Function is 9 minutes.
You delegate the long time execution job to another Cloud Function and, then, you send the response. For delegating the job to another Cloud Function, you should use Pub/Sub. See Pub/Sub triggers, the sample quickstart, and this SO thread for more details on how to implement that. In the Pub/Sub triggered Function, when the job is done you can inform the user via an email, a notification, the update of a Firestore document on which you have set a listener, etc... If generateZipWithDocuments() takes a long time, it is clearly the most user friendly option.

Firebase functions logging does not work from non-callable functions

I'm getting started with Firebase Functions. I made a simple callable function:
exports.myCallable = functions.https.onCall((data, context) => {
console.log('I am here.');
functions.logger.log('I am here 2.');
});
As expected, I see these logs in the Logs tab in Firebase Console.
However, I also made this function, which should trigger when the user creates their account.
exports.setGroupAfterAuth = functions.auth.user().onCreate((user) => {
console.log('I am here 3.');
functions.logger.log('I am here 4.');
});
In this case, I see that the function was called in Firebase Console, but I do not see my logs.
Why do the logs appear in Firebase Console for the first function but not the second, and what can I do to make them appear in the second?
It's not possible to use the Firebase Functions client SDK to invoke an auth onCreate function.
Auth onCreate functions can only be invoked by actually creating a new user account. They cannot be invoked directly from your app.
The client SDK can only be used to invoke callable functions, as described in the linked documentation. The funciton must be declared with functions.https.onCall.

Cloud Functions: callback is not a function

According to https://cloud.google.com/functions/docs/writing/background
You use background functions when you want to have your Cloud Function invoked indirectly in response to an event, such as a message on a Cloud Pub/Sub topic, a change in a Cloud Storage bucket, or a Firebase event.
And the function paramaters are (data, context, callback): https://cloud.google.com/functions/docs/writing/background#function_parameters
However, when I write a simple function like
exports = module.exports = functions.firestore
.document(path)
.onWrite((change, context, callback) => {
callback()
return
})
I get an error that says
TypeError: callback is not a function
Is callback not part of Firestore background functions? The documentation says it is
If not, is there anyway to immediately exit a function?
The Firebase API is different than the Google Cloud API. What you linked to was the Cloud API, which accepts a callback parameter. The Firebase API which you are actually using does not. The Firebase API for background functions requires you to return a promise that resolves after all the work is complete. There is no callback to call in that case.

Resources