Can I write my Cloud Functions in separate files?

First Published: 15 January 2018
Updated on: 24 September 2018

Right after I shared one of my first Cloud Functions articles on Twitter, my buddy, Justin asked me a question:

Cloud Functions multiple files

He wanted to know if there was a way to keep Cloud Functions on separate files, I hadn’t given it much thought since I was using mostly a couple of functions per app.

So my answer was “I don’t know, I think so, but I don’t know…”

I told Justin I was going to play with it during the weekend, which was a lie, I ended up playing Super Mario Odyssey instead, but on Monday I set up to achieve this.

My thinking was since it’s a standard node environment written in TypeScript, then import/export should work.

Today you’ll learn how to refactor your Cloud Functions to separate them into multiple files. We’ll start with this code:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

admin.initializeApp(functions.config().firebase);

exports.addGuestToPublicList = functions.database
  .ref(`/userProfile/{userId}/eventList/{eventId}/guestList/{newGuest}`)
  .onCreate(event => {
    const guestId: string = event.params.newGuest;
    const guestName: string = event.data.val().guestName;

    return admin
      .database()
      .ref(`/publicGuestList/${guestId}`)
      .set({ guestName });
  });

exports.addGuestToPrivateList = functions.database
  .ref(`/userProfile/{userId}/eventList/{eventId}/guestList/{newGuest}`)
  .onCreate(event => {
    const guestId: string = event.params.newGuest;
    const eventId: string = event.params.eventId;
    const userId: string = event.params.userId;
    const guestName: string = event.data.val().guestName;

    return admin
      .database()
      .ref(`/userProfile/${userId}/guestList/${guestId}`)
      .set({ guestName, eventId });
  });

Let’s say I have an app that handles events for a user, and they keep a guest list on each event they create.

The user now wants to duplicate the guest data, and they want to create a private list that shows all of their guests’ names with the event they attended. And a public list that shows only the guest names.

We could keep them both inside the index.ts file and that would be it, but for this example, we will move each function to its file (Which will make adding more functions easier).

The first thing we want to do is to create the actual files, inside the functions/src/ folder (where you see the index.ts file) let’s create two new files: public-list.ts and private-list.ts.

Inside the public-list.ts file import the admin SDK and export a function called addPublic() that takes the event as a parameter and has all the logic of addGuestToPublicList.

import * as admin from 'firebase-admin';

export function addPublic(event) {
  const guestId: string = event.params.newGuest;
  const guestName: string = event.data.val().guestName;

  return admin
    .database()
    .ref(`/publicGuestList/${guestId}`)
    .set({ guestName });
}

Now do the same for private-list.ts but this one will hold the logic of the addGuestToPrivateList function:

import * as admin from 'firebase-admin';

export function addPrivate(event) {
  const guestId: string = event.params.newGuest;
  const eventId: string = event.params.eventId;
  const userId: string = event.params.userId;
  const guestName: string = event.data.val().guestName;

  return admin
    .database()
    .ref(`/userProfile/${userId}/guestList/${guestId}`)
    .set({ guestName, eventId });
}

Now we can move to refactor index.ts, go ahead and import both functions you just created:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

import { addPrivate } from './private-list';
import { addPublic } from './public-list';

Now, we’ll call our functions as we were doing before, but inside the onCreate we will call the handler we just created:

exports.addGuestToPublicList = functions.database
  .ref(`/userProfile/{userId}/eventList/{eventId}/guestList/{newGuest}`)
  .onCreate(addPublic);

exports.addGuestToPrivateList = functions.database
  .ref(`/userProfile/{userId}/eventList/{eventId}/guestList/{newGuest}`)
  .onCreate(addPrivate);

In the end, the entire index.ts should look like this:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

import { addPrivate } from './private-list';
import { addPublic } from './public-list';

admin.initializeApp(functions.config().firebase);

exports.addGuestToPublicList = functions.database
  .ref(`/userProfile/{userId}/eventList/{eventId}/guestList/{newGuest}`)
  .onCreate(addPublic);

exports.addGuestToPrivateList = functions.database
  .ref(`/userProfile/{userId}/eventList/{eventId}/guestList/{newGuest}`)
  .onCreate(addPrivate);

Now, whenever you want to add a function, you create its file and call it from inside of index.ts.

If you have any questions don’t forget you can always leave a comment below.

If you want to take a deeper dive on Ionic + Firestore you should go through my FREE course: Build your first Firestore powered Ionic app.