Go ServerLess with Firebase cloud functions

Firebase Cloud function

With announcement of cloud functions beta at Google cloud next 2017 event, Google has added one of the highly requested features in the firebase suite. This is one major step from Google in making firebase serverless. In this post, we will see some of the capabilities, pros and cons, setup and deployment of firebase cloud functions. Google IO is just days away and knowing about firebase is surely going to help in understanding the upcoming firebase features.

Serverless computing, also known as function as a service (FaaS), is a cloud computing code execution model in which the cloud provider fully manages the starting and stopping of a function’s container as necessary to serve request. Rather than per virtual machine, requests are billed by an abstract measure of resources required to satisfy the request. The name serverless architecture / computing does not mean no servers are involved, it means the maintenance, scaling and management of servers is handled by the provider so developers can focus on single purpose service and core business problems. The first major serverless offering was back in 2014 by Amazon as AWS Lambda.

Firebase cloud functions (Beta) are relatively new (only a couple of months). Pricing is in 3 tiers, the free tier includes a generous usage limit so you can take an informed decision after trying it out. Since it is backed by Google there are easy integrations available with google services like Compute engine and analytics. According to official documentation, it is a glue between firebase and cloud services (outbound networking not available in free tier).

And now to answer the big question, how does it work? You write a piece of JavaScript code (that exposes some functions) that gets stored in Google cloud and runs in a managed node.js environment. These cloud functions are triggered by changes in database, storage; analytics events, new user signups etc. Firebase even provides a way to make it http triggered. Here are few of the capabilities of cloud functions:

  • Real-time database triggers
  • Firebase authentication triggers.
  • Firebase analytics triggers.
  • Cloud storage triggers.
  • Cloud Pub/Sub triggers.
  • HTTP Triggers.

This is a powerful set of features, here are a few of the use cases:

  • Sanitize abusive text content from database (with real-time database triggers).
  • Send welcome email to new users (with authentication triggers).
  • Send a gift coupon as push notification to a user who just purchased something from your app (Firebase analytics triggers).
  • Create and store thumbnail of images, filter out abusive images (Cloud storage triggers, with cloud vision API).
  • Use networking library of your choice to trigger cloud function to send push notification, get data etc. (HTTP triggers).

Setting up firebase cloud functions: (Assuming you already have a firebase base setup in console)

  • Install node.js and npm (https://nodejs.org/en/)
  • Install the latest firebase command line interface (CLI)
    • npm install -g firebase-tools
  • Log in to firebase
    • firebase login
  • initialize firebase project workspace
    • firebase init

      (pick only functions, default selection includes database and hosting as well, select yes for installing dependencies)

This initializes the current directory (that your terminal is pointing to) as firebase workspace for the selected project and creates a directory named functions. Functions directory has the following contents:

  • package.json – Used by node to describe the project. All the project dependencies are listed here. By default it lists “firebase-admin” and “firebase-functions” as dependencies.
  • node_modules – This is where npm keeps all its dependencies. If you have worked with node.js you already know about this. No need to checkin the entire folder on source control.
  • index.js – This is the entry point for defining all the cloud functions. This is where the server code resides.

Once this is done, create your cloud functions in index.js. Below are a few examples. Lets see some examples first before deploying the cloud functions.

HTTP triggered cloud function:

This cloud function is just performing some random task based on the request. Like reject PUT requests, return user count from real-time database with or without the name parameter that gets passed in the post request. The point is: It can be triggered via a REST end point and has access to firebase features like real-time database.

'use strict'
var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

// http triggered cloud function
exports.triggerHttp = functions.https.onRequest((req, resp) => {
    // Forbid PUT requests
    if (req.method === 'PUT') {
        console.log("Forbidden response sent");
        resp.status(403).send('Forbidden!');
    }
    if (req.body.name) {
        return admin.database().ref('/users')
         .once('value').then(allUsers => {
             console.log("NAMED post body");
             resp.send('hi '+req.body.name+'. Total users: ' + 
             allUsers.numChildren());
         });
    } else {
       return admin.database().ref('/users')
           .once('value').then(allUsers => {
              console.log("no name post body. user count sent");
              resp.send('Empty post body. Total users: ' +      
              allUsers.numChildren());
            });
    }
});

Real-time database triggered cloud function:

These type of triggers can be attached to any node of the firebase real time no-sql JSON database. For every change in that node, the function is triggered. Here is an example of use case discussed above, sanitize abusive text from database.

Sanitize database input

// Sanitizing comments.
exports.sanitizeComments = functions.database.
    ref('/comments/{commentId}').onWrite(event => {

    const commentId = event.params.commentId;
    const post = event.data.val();
    console.log('commentId: ' + commentId + ": " + post.text);

    if(post.sanitized){
        // prevent infinite looping
        return;
    }

    post.sanitized = true;
    post.text = sanitize(post.text);
    return event.data.ref.set(post);
});

there are a couple of points to note here. The trigger is attached to node root/comment/commentId which has the following child structure

{"artifactId": true,
    "text": "This is a sample comment. change crazy to lovely and stupid to wonderful",
    "commenter": "user_20jd2j2jfj",
    "sanitized": false,
    "timestamp": 1493815657159
}

So for every comment that gets added we can extract the comment id (event.params.commentId)  and data (event.data.val()) from data as shown in JavaScript function above. As soon as this comment is added (the JSON above), the function is triggered and it overwrites the data with a sanitized text. To prevent infinite looping (as writing sanitized data in database will again trigger the function and will keep on going till your quota is exhausted) the if check with sanitized flag is in place. Now since writing to database is an asynchronous operation, it returns right away before performing the write operation. To handle this, cloud functions must return a JS Promise, which is succeeds or fails when the asynchronous operation (like write) is complete. This enables firebase to decide whether or not the operation has been terminated. Refer this link to learn more about JavaScript promises.

In one my apps I am using real time database triggers to send push notification about a new post. Image below show sending FCM whenever a user gains a new follower:

database_triggered_FCM

Firebase Auth triggered cloud function:

Among many offerings, firebase also provides an auth SDK that enables app developers to integrate social login with major identity providers like facebook, google, twitter etc. Once a user logs in with one of these or signs up via email (the firebase way) an event is emitted, this can be used to send welcome email to new users.

exports.sendWelcomeEmail = functions.auth.user()
    .onCreate(event => {
        const user = event.data; // The Firebase user.
        const email = user.email; // The email of the user.
        const displayName = user.displayName; // The display name of the user.
        return sendWelcomeEmail(email, displayName);
});

The function definition states that this event will be triggered when a new user is created. To implement sendWelcomeEmail function any 3rd party providers (paid tiers) or gmail can be used.

Firebase storage triggered (complex use case):

The image below is self explanatory:

Storage_triggered_cloud_function

Deploying cloud functions:

  • deploy all – database rules, storage rules, functions and hosting data
    • firebase deploy
  • Deploy partially
    • firebase deploy --only functions

Deployment can take several minutes. After successful deployment, you should see a screen similar to this:

cloud_function_deployed

Notice the function URL for http triggered cloud function. Use this as http end point for triggering.

The Pros:

  • Cloud functions are very helpful in keeping all the business logic centralized in one place and not in multiple clients.
  • Firebase real-time database makes it insanely easy to develop MVP. Database triggered cloud functions enables solving many issues that will otherwise need an app update.
  • No need to learn any other language or server maintenance (that is why it is serverless)
  • Value for money: free and paid quota are much better than what is available out there.
  • Single command for initialization and deployment.

The Cons:

  • Maintaining cloud server code can become a nightmare as your business logic becomes complex.
  • No control over instances.
  • In case of an error like the infinite loop issue mentioned above, there is no alert.
  • Firebase doesn’t support complex database queries yet, this restricts what can be done with firebase functions.
  • Only one language choice as of now.

I have open sourced 2 apps that uses firebase:

  • SyncroEdit:  A collaborative note editing android app that uses realtime database (check it out here)
  • Vividity: Photo sharing android app using firebase (check it out here). This has an index.js file at its root which covers some of the use cases discussed here.

Let me know what you think. Got any questions? Shoot below in comments.

-Kaushal Dhruw (@drulabs twitter/github)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s