How to use Firebase Callable Cloud Functions with Ionic

Tools used:
Backend: Firebase -- Version: Evergreen
Frontend: Ionic -- Version: 5.0.0

This is a guest post from Simon Grimm, Ionic Developer Expert and educator at the Ionic Academy. Simon also writes about Ionic frequently on his blog Devdactic.

Since quite some time now, you can create cloud functions within Firebase. You might have used them to react to changes in your Firestore database, but actually there’s more you can do with this powerful feature!

Demo screen

In this tutorial we’ll create and deploy a cloud function that’s not tied to any change in the database - it’s simply an endpoint you can call from anywhere, just like you use other APIs.

We’ll build a simple Ionic app with AngularFire to showcase the behaviour and implement the complete flow of events.

Firebase Preparation

Before we get into the code, make sure you actually have a Firebase app, so create a new project in your Firebase console or use any existing project you might already have.

Firebase new app console

In case you need help setting this up, just check out the getting started guide on Devdactic.

After you have created the project we also need to get the Firebase configuration for our Ionic app, so visit the Project settings (gear icon at the top left) and from the general tab create a web app.

This will show you a configuration object, which we need for the next step.

Ionic Project Configuration

Now we can create a simple Ionic app to test out the cloud functions (and to actually create them). Go ahead and start a new project like this:

ionic start ionicCloud blank --type=angular
cd ./ionicCloud
ng add @angular/fire

Since version 6 of AngularFire we can use an Angular schematic to add it to our project, so integrating it is now simply one ng add call in our setup process!

This new AngularFire version will also only work with Angular 9, so if your app is still on an older version you should now update it by running:

ng update @angular/cli @angular/core

Now we can also add the Firebase config we obtained after adding the web app to our Firebase project. You can paste the value like this into your environments/environment.ts:

export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: '',
    authDomain: '',
    databaseURL: '',
    projectId: '',
    storageBucket: '',
    messagingSenderId: '',
    appId: ''
  }
};

FYI: If you are a regular user of Firebase, you can give my KickoffIonic prototype a try - it helps you to easily bootstrap an Ionic app with Firebase connection, pages with entities and CRUD functionality right out of the box!

To finally connect everything, we just need to load the main module of AngularFire and every other feature module we might need.

In our case, that’s only the AngularFireFunctionsModule for now, so go ahead and change your app/app.module.ts to:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { AngularFireModule } from '@angular/fire';
import {
  AngularFireFunctionsModule,
  FUNCTIONS_REGION
} from '@angular/fire/functions';
import { environment } from './../environments/environment';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFireFunctionsModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    { provide: FUNCTIONS_REGION, useValue: 'us-central1' }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

You might have noticed the value we provided to the FUNCTIONS_REGION in the array of providers? This value actually depends on where your functions are deployed! You can leave it for now like this, you’ll see a log later on once we deploy the functions to Firebase anyway. Just keep in mind that you might have to go back to this step and change the value in here.

Firebase Callable Function

Now the interesting part begins, as we integrate a Firebase project in our Ionic app. At the root of your Ionic app simply run:

firebase init functions

This command will first ask for the project you wanna use, so select the Firebase project from the beginning.

For other questions, use TypeScript and set up linting and go ahead to install all necessary dependencies right now!

The result is a new folder functions right inside your Ionic project, in which you can create your Firebase cloud functions.

To create our first callable cloud function, simply change the content of your functions/src/index.ts to:

import * as functions from 'firebase-functions';

export const myUppercaseFunction = functions.https.onCall((data, context) => {
  return { msg: data.coolMsg.toUpperCase(), date: new Date().getTime() };
});

It’s actually that easy!

We define a function called “myUppercaseFunction” (the name is actually important later again), and the function simply returns a new object.

This object contains data that was passed to this function and transforms a string to uppercase and also the current time. I know, not a super helpful function but enough to see how everything plays together.

To deploy our new function, you can now use the firebase CLI again and call:

firebase deploy --only functions

Since we only need to deploy our functions that’s all you need to run, and the output will show something like this:

Demo screen

At this point you can also see in which region your functions are deployed, so make sure you update your app.module.ts with this value if it’s different from what you copied in the beginning.

Calling a Cloud Function

The function is deployed to Firebase and ready to be called from any app or service. To make this even easier, we can now use the AngularFireFunctions package from AngularFire and directly call a cloud function by its name!

Imagine this like a standard Http call, and of course you can pass data to the function. In our case, we keep a simple variable myInput in our class and pass it to the function.

The only thing you have to watch for is the usage of the function, since it’s first only a callable in which we need to use the name of the function from our previous step.

Only when you call this callable, it actually returns an Observable to which you can now subscribe and handle the result as usual.

Go ahead with the following code for your home/home.page.ts:

import { Component } from '@angular/core';
import { AngularFireFunctions } from '@angular/fire/functions';
import { AlertController } from '@ionic/angular';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  myInput = '';

  constructor(private functions: AngularFireFunctions, private alertController: AlertController) {}

  callCloudFunction() {
    // Use the function name from Firebase
    const callable = this.functions.httpsCallable('myUppercaseFunction');

    // Create an Observable and pass any data you want to the function
    const obs = callable({ coolMsg: this.myInput });

    obs.subscribe(async res => {
         const alert = await this.alertController.create({
           header: `Time: ${res.date}`,
           message: res.msg,
           buttons: ['OK']
         });
         await alert.present();
    });
  }
}

Finally the last step is to hook up our actions with a simple view - I guess this needs no further explanation!

Open your home/home.page.html and change it to:

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>
      Ionic Cloud Functions
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-item>
    <ion-label position="stacked">Send to cloud function:</ion-label>
    <ion-input [(ngModel)]="myInput"></ion-input>
  </ion-item>
  <ion-button expand="full" (click)="callCloudFunction()"
    >Call cloud function!</ion-button
  >
</ion-content>

Now you can run your Ionic app, insert some text and click the button - as a result you should see an alert with the text in uppercase and a current timestamp!

Conclusion

This example is not a real use case, why should I use a callable cloud function?

Imagine you want to use an SDK like Stripe, for which you have a private key. You don’t want to expose this key in your Ionic app, which is basically only a website.

So you can keep the private key in your Firebase environment (there’s really an environment you can write to, not the database!), and basically route the calls to the Stripe through your cloud function and keep everything safe!

That’s just one useful example, you could also perform all kinds of data operations on your Firestore, return specific combined data that’s otherwise hard to retrieve from your collections or finally - create a whole API simple inside the cloud!

If you are interested in more courses on Ionic, make sure to also check out my Ionic Academy.