Create Cloud Functions in Firebase

Authorization, CRUD, queries, most of the things come out of the box with Firestore. However, sometimes there is a need to make some modifications which would be too sensitive to do in the browser.

Authorization, CRUD, queries, most of the things come out of the box with Firestore. However, sometimes there is a need to make some modifications to the objects, which would be too sensitive to do in the browser. To demonstrate, let's look at the following object:

{
    imageUrl: '',
    price: 9.90,
    title: 'Homemade Soup'
}

When the user adds a new product, I want to calculate a slack property for it, so products could be displayed by their slack in the URL. This should be unique, but also changeable. I don't want my frontend to calculate this, as a duplicated or missing key could cause problems. To do this on the backend, use a cloud function. Using Firebase CLI, it takes only a few minutes to set this up.

npm install -g firebase-tools
firebase login 
firebase init functions

When the installer asks, choose TypeScript, add TSLint, and also install npm dependencies.

It will create a functions/src folder, with an index.ts in it. The function below will add a product to my Firestore database, with my additional property attached.

exports.addProduct = functions.https.onCall(
    (data, context) =>
       admin
           .firestore()
           .collection("/products")
           .add({
                ...data,
                slack: slackify(data.title)
            })
      )
      .then((docRef) => docRef.id) // be careful what returns
);

Be extremely careful with the last line though. It took me some time and obscure error messages to find out what was happening. When the function inside functions.https.onCall returns a promise, Firebase tries to serialize the returned object. This function returned a DocumentReference, what as it turned out, is not serializable and causes a stack overflow. To tackle this, I return the id of the newly created document.

When everything is done, it's time to deploy.

firebase deploy –-only functions

On the Firebase site, you can inspect executions and error stacks.

Calling a Cloud Function in Angular

Like Firestore or all the other Firebase modules, FireFunctions has to be imported the same way, in your module.

import { AngularFireModule } from "@angular/fire";
import { AngularFireAnalyticsModule } from "@angular/fire/analytics";
import { AngularFirestoreModule } from "@angular/fire/firestore";
import { AngularFireFunctionsModule } from "@angular/fire/functions";

@NgModule({
    declarations: [
        AppComponent,
        //...
    ],
    imports: [
        // ...
        AngularFireModule.initializeApp(firebaseConfig),
        AngularFirestoreModule.enablePersistence(),
        AngularFireAnalyticsModule,
        AngularFireFunctionsModule,
    ],
    bootstrap: [AppComponent],
})
export class AppModule {}

In your service, inject AngularFireFunctions, and everything is ready to go:

//...
import { AngularFireFunctions } from "@angular/fire/functions";

@Injectable({
    providedIn: "root",
})
export class ProductService {

    constructor(
        private fireFunctions: AngularFireFunctions
    ) { }

    addProduct(product: CreateProduct): Observable<any> {
        return this.fireFunctions.httpsCallable("addProduct")(product);
    }
}