Firebase Cloud Functions

Revision as of 15:08, 30 August 2017 by Neil (Talk | contribs)

Revision as of 15:08, 30 August 2017 by Neil (Talk | contribs)

PreviousTable of ContentsNext
Firebase Performance MonitoringInstalling the Firebase CLI



Firebase Cloud Functions are essentially JavaScript functions hosted on the Google cloud. These functions are triggered in response to Firebase related events such as data being written to a Firebase realtime database or the creation of a user account using Firebase authentication.

This chapter will introduce the key concepts of Firebase Cloud Functions.

A Cloud Functions Overview

Cloud Functions can be thought of server side code that is executed in response to events triggered by other Firebase services. A Cloud Function could, for example, be used to send a welcome email message to new users in response to user account creation events, or to send a Firebase Notification message when a particular value is written to a node in a realtime database.

Cloud Functions are written in JavaScript using Node.js and the Firebase Cloud Functions SDK and, once written, are deployed to the Google Cloud Platform using a command-line tool known as the Firebase CLI.

Once the functions have been deployed, Google Cloud performs all of the management tasks to ensure that the functions are triggered in response to events occurring on the Firebase and Google Cloud platforms. Each cloud function runs within a virtual server managed by Google Cloud which automatically adjusts the number of virtual servers for each function in response to changes in demand.

Deployed functions may be updated at any time using the Firebase CLI and detailed logging information is available both from the command line and within the Firebase console. Firebase Cloud Functions can be triggered by events from any of the following triggers:

Authentication – User account creation and deletion.

Realtime Database - Database write operations.

Cloud Storage – Uploading, deleting and updating of files.

Google Analytics for Firebase – Conversion events such as an in-app purchase.

Google Cloud Pub/Sub – Transmission of a new Pub/Sub message.

HTTP – HTTP request to a specified URL (includes GET, POST, PUT, DELETE and OPTIONS).

Firebase Authentication Functions

A cloud function can be triggered in response to the creation or deletion of Firebase Authentication based user accounts. Authentication cloud functions are implemented using the functions.auth.user().onCreate() and functions.auth.user().onDelete() event handlers.

The event data passed to the JavaScript function when it is called contains all of the attributes relating to the new user including email, display name, photo URL, authentication provider and whether or not the email address has been verified. The following is an example function designed to be triggered by account creation events:

exports.newUserAdded = functions.auth.user().onCreate(event => {

        const user = event.data;
        const email = user.email;
	const displayname = user.displayName;
	const photoUrl = user.photoURL;
.
.
});

A function to handle account deletion events is similarly structured:

exports.userDeleted = functions.auth.user().onCreate(event => {
.
.
});

Realtime Database Functions

Realtime database cloud functions are triggered by write operations performed on a database tree at any point beneath a specified reference point and are implemented using the functions.database.ref().onWrite() event handler. The database path for which write operations are to trigger the function is specified via a ref() call, for example:

exports.userStatusChange = functions.database.ref('/users/profile') 
	.onWrite(event => {
.
.
}; 

The function will then be triggered by a write operation that occurs at the /users/profile path in the database tree including all child nodes (for example data written to /user/profile/email will also trigger the function call).

The reference path may also include wildcards by placing the path component in braces ({}):

exports.userStatusChange = functions.database.ref('/users/{userId}/email') 
	.onWrite(event => {

When the function is called, it is passed event data in the form of a DeltaSnapshot object. This contains both the new value being written and the previous value that is being overwritten:

exports.userStatusChange = functions.database.ref('/users/profile') 
	.onWrite(event => {

	const newValue = event.data.val();
	const previousValue = event.data.previous();
.
.
};

Much like the DataSnapshot objects used when working with Realtime Databases in Android Java code, the DeltaSnapshot can be navigated to access specific nodes of the tree:

var snapshot = event.data.child('email');

To detect the removal of data from the database, simply call the exists() method of the event data. If no data exists, the cloud function is being called in response to a data deletion event within the Firebase database:

if (event.data.exists()) {
	// New data is being written to the database
else {
	// Data was deleted from the database
}

Google Analytics Functions

Any Firebase Analytics event that has been configured as a conversion can be used to trigger a cloud function. As outlined in the earlier chapters covering analytics, events can be marked as conversions using the switch highlighted in Figure 52‑1 within the Events screen of the Firebase console Analytics screen:


Cloud functions console.png

Figure 52‑1


This allows functions to be triggered based on just about any type of activity taking place within an app as long as that activity is associated with an analytics conversion event. Analytics cloud functions are implemented using the functions.analytics.event() event handler, for example:

exports.userUpgraded = functions.analytics.event('customEvent') 
	.onWrite(event => {
.
.

Included with the event data passed to the function when it is called are all of the parameters and user properties associated with the analytics event.

HTTP Functions

HTTP cloud functions are triggered in response to an HTTP request and are implemented using the functions.https.onRequest() event handler. When called, the function is passed request and response objects from which information such as the request method (POST, GET etc.) and query values may be extracted.

HTTP cloud functions should self-terminate via a call to either send(), redirect() or end():

exports.httpFunction = functions.https.onRequest((request, response) => {
   const name = request.query.name;
   console.log("Name = " + name);
   response.send("Hello from Firebase, " + name);
});

Once an HTTP cloud function has been deployed, it is assigned a unique URL via which it can be triggered. An example HTTP cloud function will be implemented in the next chapter (Installing the Firebase CLI).

Cloud Storage Functions

Cloud Storage functions are triggered when a file is uploaded using Cloud Storage, or when an existing file is updated or deleted. Once triggered, the function is also able to download a copy of the file, modify it and upload the new version back to cloud storage. Cloud Functions also have access to the ImageMagick utility which can be used to transform downloaded image files.

Cloud Storage Functions are implemented using the functions.storage.object and functions.storage.bucket().object() event handlers, the latter being used to reference a specific storage bucket, for example:

exports.makeMonochrome =  
     functions.storage.bucket('images').object().onChange(event => {
.
.
.
});

The event data passed through to the function includes all of the properties of the file, including bucket name, file path, content type, resource state, download link, file size and timestamp:

exports.makeMonochrome =  
     functions.storage.bucket('images').object().onChange(event => {

     const fileObject = event.data;

     const filePath = fileObject.name;
     const bucket = fileObject.bucket;
     const contentType = fileObject.contentType;
     const resourceState = fileObject.resourceState;
     const size = fileObject.size;
     const timeCreated = fileObject.timeCreated;
     const mediaLink = fileObject.mediaLink;
.
.
});

For performing file downloads and uploads from within a cloud function, the Google Cloud Storage package is recommended. This can be installed using npm as follows:

install --save @google-cloud/storage

This package should then be imported at the top of the .js file containing the cloud function:

const gcs = require('@google-cloud/storage')();

Clearly, a storage cloud function is triggered in response to a change being made to a specific file in cloud storage. This file can be downloaded to the virtual server containing the cloud function using the following code:

const object = event.data;
const fileBucket = object.bucket;
const filePath = object.name;
const bucket = gcs.bucket(fileBucket);
const tempFilePath = `/tmp/mypic.png`;

return bucket.file(filePath).download({destination: tempFilePath
              }).then(() => {

       // Code to be executed when the download finishes

});

The then() call in the above example is part of the handling mechanism for a JavaScript Promise. Promises are often used when a task is performed asynchronously and may not be completed immediately. The download() method used above, for example, returns a Promise object when it is called. By calling then() method on that Promise object, we are able to specify the code that is to be executed when the download either completes or fails. A promise can be thought of as an alternative to the listeners or completion handlers that typically have to be implemented to handle the result of an asynchronous operation. All Firebase JavaScript functions that perform an asynchronous task now support both promises and the traditional completion handler methods.

Note also that the Promise from the above download operation is returned. This returns the Promise from the download call to the code that originally invoked the cloud function. The calling code may then also call the then() method on the Promise object to define the actions to be performed on completion. This technique also ensures that the cloud function is kept alive until the asynchronous task is complete. Failing to do this runs the risk of the function exiting and the virtual machine shutting down before the asynchronous task finishes.

To upload a file, simply call the upload function:

return bucket.upload(tempFilePath, {destination: newFilePath
              }).then(() => {
       // Code to be executed when the upload finishes
});

When uploading files to cloud storage from within a cloud function it is important to remember that the upload will trigger another call to the cloud function (since it counts as a change to a file). To avoid a recursive loop, be sure to add some defensive code to exit from the cloud function in the event that the file change was caused by the function itself. An example of how to achieve this will be covered in the chapter entitled A Cloud Functions and Firebase Cloud Storage Example.

It is also important to distinguish file update events from file deletions. If an event is being triggered due to a file being deleted from cloud storage, the resourceState property of the event data will be set to “not_exists” allowing defensive code similar to the following to be written to return from the function if the file has been deleted:

const object = event.data;
const resourceState = object.resourceState;
 
if (resourceState === 'not_exists') {
     // This is a file deletion event
     return;
}

Performing External Operations

Any external file operations performed within a cloud function (such as running the ImageMagick convert utility) should be spawned as a child object and returned as a Promise from within the function. This makes use of the Child Process Promise package which must be installed as follows:

install –save child-process-promise

Once installed, the package must also be imported within the cloud function .js file within which the spawn operation is to be performed:

const spawn = require('child-process-promise').spawn;

The following code fragment demonstrates the use of child process spawning to perform an image conversion using the ImageMagick convert utility:

return spawn('convert', [tempFilePath, '-monochrome', tempFilePath])
		.then(() => {
        // Code to be executed after the conversion completes
});

As with many functions, the spawn function returns a Promise, allowing code to be specified which will be called when the task completes.

Modifying Cloud Function Settings

Once a cloud function has been deployed the Firebase console provides a list of deployed functions and access to the logs generated during execution. Additional information about deployed cloud functions is available within the Google Cloud Platform Console (https://console.cloud.google.com).

In addition to greater detail of metrics such as memory usage, quota usage and execution time, this console also provides the ability to edit properties relating to the function such as the amount of memory allocated to the virtual machines within which the function executes (a topic which will be covered in the chapter entitled A Cloud Functions and Firebase Cloud Storage Example).

Summary

Cloud functions are JavaScript functions that reside in small virtual machines hosted in the Google Cloud. These functions are triggered in response to certain Firebase related events that take place within an app. Cloud functions are written using Node.js and deployed to the Google Cloud Platform using the Firebase CLI tool. Once deployed, all of the work involved in detecting events, triggering the functions and provisioning virtual machines is handled by the Google cloud.




PreviousTable of ContentsNext
Firebase Performance MonitoringInstalling the Firebase CLI