Firebase Cloud Functions, (or running code on Firebase Servers!)

First Published: 23 March 2017
Updated on: 18 June 2019

Taken straight from the official website:

Firebase Cloud Functions is a hosted, private, and scalable Node.js environment where you can run JavaScript code. Firebase SDK for Cloud Functions integrates the Firebase platform by letting you write code that responds to events and invokes functionality exposed by other Firebase features.

So, what exactly are Firebase Cloud Functions?

They are a hosted, private, and scalable Node.js environment where you can run JavaScript code. In the past, if you needed to do things like creating a user account based on changes in the database, or send push notifications you had to create your server, most people went for a nodejs app on Heroku, and it was working pretty good for them.

The downside of it is that you probably chose a Backend as a Service because you wanted to focus on writing your app, instead of focusing on maintaining servers, handling security, uptime, scalability, and more.

That’s why Firebase created Cloud Functions, now they’re running the server for you, all you need to do is write the JavaScript function, and they’ll take care of setting up the server, scalability, uptime, security, etc.

For a mobile developer, this is great, and it means that you no longer have to deploy your server, you only deploy your functions and are done.

Another AWESOME thing about it, the Firebase team created an SDK for cloud functions, which means that you don’t have to write custom code to connect to your database, they provide the triggers, it currently supports:

  • Real-time Database Triggers => Execute functions on database changes.
  • Firestore Triggers => Execute functions on Firestore changes.
  • Firebase Authentication Triggers => Run functions on user creation, update, or delete.
  • Firebase Analytics Triggers => Run functions when analytic goals trigger.
  • Cloud Storage Triggers => Want to resize images when they’re uploaded to Firebase Storage? No problemo.
  • HTTP Triggers => You get a basic get/post URL where you can call it and run a function.

How does it work?

After you write and deploy a Cloud Function, Google’s servers begin to manage the function immediately, listening for events and running the function when it is triggered. As the load increases or decreases, Google responds by rapidly scaling the number of virtual server instances needed to run your function.

The basic lifecycle of a Cloud Function is:

  • The developer writes code for a new Cloud Function, selecting an event provider (such as Real-time Database), and defining the conditions under which the Cloud Function should execute.
  • The developer deploys the Cloud Function, and Firebase connects it to the selected event provider.
  • When the event provider generates an event that matches the Cloud Function’s conditions, the code is invoked.
  • If the Cloud Function is busy handling many events, Google creates more instances to handle work faster. If the Cloud Function is idle, instances are cleaned up.
  • When the developer updates the Cloud Function by deploying updated code, all instances of the old version are cleaned up and replaced by new instances.
  • When a developer deletes the Cloud Function, all instances are cleaned up, and the connection between the Cloud Function and the event provider is removed.

Cloud Functions in Action

We’re going to go through the entire work-flow of creating and deploying a cloud function for an app, imagine you have an app for Fitness trainers, where they need to create accounts for they clients to help them get shredded (Disclaimer An app can’t help you get shredded, you have to actually lift the weights).

So, whenever the coach adds someone to the database as a client, we’ll have a cloud function listening on that node and create an account for that person.

To start writing Cloud Functions (by the way, let’s call them CF from now on, that way we avoid all the extra typing) we need to install the Firebase CLI, go ahead and open your terminal and type

npm install -g firebase-tools

Depending on your operating system (mostly if you use Linux) you might have to add sudo before running that line of code.

Now, we need to login into our Firebase CLI, so that we have access to our apps, for that we need to run:

firebase login

Since our Firebase account is connected to our Google account, that command will open a browser window for us to login to our Gmail/Gsuite account and authorize Firebase. Once you do that, the CLI will log in, and you’ll be able to access its full power.

Now, I’m going to assume you’re into your app folder in the terminal, because you know, you’re working on a fitness app, first, get out of your app’s folder, and then create a new folder for your CF:

cd ..
mkdir functions
cd functions

That will leave you with 2 folders, one for the app and one for functions, both folders are at the same level (tho they don’t have to, they can be in different folders if you want) and then it will take you inside the functions folder, where you’ll initialize your functions:

firebase init functions

That command will show a list of the applications that exist in the Firebase Console, and you need to choose the one we’re working on so that the initialization connects the firebase admin to that specific application.

It also asks you if you want to use JavaScript or TypeScript, I’d go with TypeScript.

When you choose an application, then it creates an entire folder structure for you inside the functions folder we created:

Firebase Cloud Functions

A few things to keep in mind:

  • It is a node.js environment, that means you can run npm install --save package_name and use whatever package you want in your functions.
  • All your functions need to be created and exported inside index.ts
  • It comes with 2 packages installed and imported:
import { functions } from 'firebase-functions';
import { admin } from 'firebase-admin';
admin.initializeApp();

firebase-functions refers to the CF SDK for Firebase, where we have built-in functions to listen to authentication or database changes.

And the firebase-admin package is the ADMIN SDK for Firebase, so we can write, read from the database, create users, and more.

Pay special attention to this line:

admin.initializeApp();

If you’ve been paying attention before, the .initializeApp() function initializes our app, same as when we use the JS SDK or AF2 in our apps.

Now it’s time to write our first function (it was exciting to run this and see the changes in the database happen without me writing to it), we’re going to create a function called createClientAccount().

The function will listen to the database, to access the database from CF we have the functions.database functions, we’ll have it listen to a specific node:

exports.createClientAccount = functions.database.ref(
  '/userProfile/{userId}/clientList/{clientId}'
);

Right there we created a reference to the database in the node:

'/userProfile/{userId}/clientList/{clientId}';

One important thing to keep in mind, the database references inside CF accept wild cards, so inside the reference, {userId} and {clientId} are wild cards, meaning that:

'/userProfile/javebratt/clientList/client1';
'/userProfile/javebratt/clientList/client2';
'/userProfile/another_weird_id/clientList/client54';
'/userProfile/another_weird_id/clientList/client49';

All of those will match that database reference, and inside the function, we’ll later be able to grab those values.

Now we need to trigger our function, and this might seem familiar to you, you know we have several triggers for database listeners in our apps, especially things like .on() or .once(), for CF we also have triggers to listen to the database, we have one called .onWrite() that will trigger when someone writes to that database node. So our function then will look like this:

exports.createClientAccount = functions.database
  .ref('/userProfile/{userId}/clientList/{clientId}')
  .onWrite((change, context) => {
    // We'll handle all the logic here
  });

That’s an active function, ready to listen to that database node and run every time someone writes data there (it will also trigger on updates or deletes to that node, since deleting the node is doing .set(null)).

The first thing we need to do is to create the new user, for that, we’re going to use the firebase admin, one quick tip, so you wrap your head about the admin SDK, is the same thing as the JS SDK, you just preface everything with admin. instead of firebase., so if in the regular JS SDK we do:

firebase.auth().createUser(credentials);

In the admin SDK we’ll do:

admin.auth().createUser(credentials);

So, go ahead and create the new user account:

exports.createClientAccount = functions.database
  .ref('/userProfile/{userId}/clientList/{clientId}')
  .onWrite((change, context) => {
    return admin
      .auth()
      .createUser({})
      .catch(error => {
        console.log('Error creating new user:', error);
      });
  });

We want to pass some information to that createUser() function, you don’t need anything, but if you send it blank it will create an anonymous account, and then it will be a pain to link it to our user’s account.

We’re going to pass several values, we want:

  • The UID, we’re not aiming to get one of Firebase automatic UIDs (user ID), we’re passing our UID, the one that Firebase creates as an ID inside the database node:
'/userProfile/{userId}/clientList/{clientId}';

So we’re taking that clientId and passing it to the function as the uid, we’re also passing the client’s email as the username/email for authentication, we’re passing the client’s name to the user object, and we’re setting up a temporary password.

You might be wondering, but Jorge, where the heck are we getting the email and name from? and I have great news, the CF SDK can access all of the information inside the '/userProfile/{userId}/clientList/{clientId}' node, it’s available in the event variable that the onWrite() function returns, so our function will look like this:

exports.createClientAccount = functions.database
  .ref('/userProfile/{userId}/clientList/{clientId}')
  .onWrite((change, context) => {
    return admin
      .auth()
      .createUser({
        uid: context.params.clientId,
        email: change.after.val().email,
        password: '123456789',
        displayName: change.after.val().fullName
      })
      .catch(error => {
        console.log('Error creating new user:', error);
      });
  });

Notice how we’re accessing the data in 2 different ways, we get access to the wild cards through the context.params interface, so at any point, we can do context.params.clientId and we get the current client’s ID to set as the new user’s uid.

And the data inside the object is available via the change.after interface, so we have access to the email address, the full name, and even the starting weight through change.after.val().property_name.

And that’s it, all we have to do now is to deploy our function to Firebase servers and BOOM, every time a coach creates a new client record in the database, that CF will trigger.

Before deploying it, I want to make sure I go over a few tips to make your life easier, and to ensure they work.

Cloud Functions can be killed

Yup, they can be killed without completing, server can kill idle functions, to avoid that we need to make sure we’re returning a promise, when you return a JavaScript Promise to a CF, that function keeps running until the promise is resolved or rejected, that way CF avoids killing your function until it either completes successfully or crashes with an error.

Avoid infinite loops at all costs

There’s something you need to pay special attention to, running an infinite loop, what would have happened (it did) if I would have decided after creating the user, to store some other information about the user inside the coaches clientList node?

It would have triggered the function again, the result of the function would invoke the call of the function, and it would run in an infinite loop.

It was the reason why I added a custom uid to the user creating instead of letting Firebase create it. My original idea was to create the user and then write that user’s uid to replace the client’s ID in the Coaches profile node. If I would have followed that path, then when the function changes the ID in the coaches node it would have triggered itself again.

Deploying your CF to Firebase

Now comes the fun part, we’re going to deploy our function to Firebase servers so it can work, this part is easy, all you need to do is open up your terminal, remember, you need to be inside the folder where you created the functions

firebase deploy --only functions

Then pay attention to your terminal, it will let you know if the function was successfully uploaded or if it crashed because there was a syntax error.

So, what do you think about Cloud Functions for Firebase? Is it something you’ll be using? Do let me know!