Js Mobile Dev About Courses Articles Youtube Services Contact

How to set up Firebase Emulator for local development

Libraries and Versions

Backend: Firebase v7.0.0
Frontend: @ionic/angular v5.0.0
Backend: @angular/fire v6.0.0

Do you know how to work locally when you're building your app? Or are you always working against your online database?

Did you know that working locally, you can not only work faster (everything is in your machine), but also spend less money? That's because the reads or writes to your local database don't count against your quota, so you end up saving money πŸ’Έ.

Firebase Emulator Suite

Today, we'll go through the process of setting up the Firebase emulator in a way that you can use it for most of your app needs.

In this article, you'll learn how to:

  • Initialize the Firebase Emulator.
  • How to use the Emulator UI to have an overview of your entire project.
  • Run Cloud Functions locally.
  • Tell your app to use the Firestore emulator when developing and the online DB when deployed.
  • Copy your online database to the emulator and creating a backup file to always start your emulator with seed data.

With that in mind, let's get started!

Initialize the Firebase Emulator

The first thing we'll do is to initialize the Firebase emulator. For that, we want to open the terminal, and navigate to our project's root folder once we're there, we can type:

firebase init emulators

It will prompt a setup 'screen' in the terminal. First, it will ask you to pick which emulators you want to use, for me, I always start with Firestore and Cloud Functions, but you can add whichever you want, depending on your project needs.

Pick the emulators to initialize

After you pick the emulators you want to use, then it will prompt you to pick the ports they'll run on so that you can do something like localhost:4000 and open them in the browser.

Leave the defaults, unless you want to change them for something specific to your project or your way of building products.

Pick the ports for each emulator

How to use the Emulator UI

Now that everything is set up open the terminal again in your project's root folder, and start the emulator using:

firebase emulators:start

It will start your emulators and show you the emulators' information in the terminal, as shown in the picture below.

Start the firebase emulator suite

Once it's running, you can open your browser and navigate to localhost:4000, remember, if you chose a different port when initializing the emulator, then use it here.

Open the Firebase emulator UI

In the UI, you'll be able to see a dashboard that shows you all your active emulators you have, you can click on any of them, and it will take you to the specific page for each one.

The Firestore emulator will show you a copy of your Firestore data as if you saw it in the Firebase Console (but it's much faster than the online console).

Open the Firebase emulator UI

The Functions emulator will show the Cloud Functions logs.

Open the Firebase emulator UI

And the hosting emulator will open your website locally. It will use the code in your public folder, the one you have set up in firebase.json.

"hosting": {
  "public": "dist/static", // This will be served in the local hosting emulator
  "ignore": [
    "firebase.json",
    "**/.*",
    "**/node_modules/**"
  ]
},

Run Cloud Functions locally

Now that you have an overview of everything through the emulator UI, you can also test your functions locally. This will happen in two different ways.

For cloud functions that trigger with Firestore changes, the local functions will trigger automatically as soon as the Firestore emulator is also running.

For Cloud Functions that act as HTTP functions, then you need to change the function URL for the one that points to the local port you added at the beginning.

If you don't remember the ports, you can always find them in firebase.json

"emulators": {
  "functions": {
    "port": 5001 // This is the functions port.
  },
  "firestore": {
    "port": 8081
  },
  "hosting": {
    "port": 5000
  },
  "ui": {
    "enabled": true,
    "port": 4000
  }
}

Your function's URL usually goes like this:

https://us-central1-<Your Project ID>.cloudfunctions.net/<Your func name>

To call the emulator one, you will change the domain to be:

https://localhost:5001/<Your Project ID>/us-central1/<Your func name>

For example, if my project's ID is javebratt, and my cloud function's name is addUser, then the online URL will look like this:

https://us-central1-javebratt.cloudfunctions.net/addUser

And the emulator (or local) URL would be:

http://localhost:5001/javebratt/us-central1/addUser

If your functions read/write data to Firestore, it will pick the emulator is the Firestore emulator is running, if not, it will pick the online database.

Let Ionic know which Firestore instance to use

For our project, we're using Ionic with the @ionic/angular package, and also @angular/fire as a helper library to interact with Firebase.

There is one configuration we need to add to our app so that when you run ionic serve or ng serve if interacts with the Firestore emulator instead of the online database.

First, you need to go to app.module.ts and find the firestore import. It looks like this:

import { AngularFirestoreModule } from '@angular/fire/firestore';

Inside of it, you need to import firestore settings. In the end, it will look like this:

import {
  AngularFirestoreModule,
  SETTINGS as FIRESTORE_SETTINGS,
} from '@angular/fire/firestore';

Once it's imported, go into the providers array and add a new provider:

import { environment } from 'src/environments/environment';

providers: [
  ...,
  {
    provide: FIRESTORE_SETTINGS,
    useValue: environment.emulator ? {
      host: 'localhost:8081',
      ssl: false
    } : undefined
  }
],

It's going to ask our app, is there an emulator property in environment, and is it set to true? If it is, it's telling our app that the Firestore host will be localhost:8081 and that we won't use SSL (https).

Remember, we're setting port 8081 there because that's the one that the CLI added for us in the configuration when we ran the emulator init command, if it created a different port for you, then use that one.

If there's no emulator property, or if it's set to false it'll be undefined and the app will use the standard Firestore configuration.

Then, go into src/environments/environment.ts and add the property:

export const environment = {
  production: false,
  emulator: true,
};

Now, whenever you run ng serve, it will read the environment.ts file and run against the emulator, and when you run ng build --prod, it will use the environment.prod.ts file and run against the online Firestore instance.

Once more thing, when you're going to serve the application, don't forget to open a separate terminal and run the emulators command:

firebase emulators:start

Copy your online database to the emulator

One thing that bugs me is that the Firestore emulator starts with an empty database. It might be useful for you, but in my case, my app depends on data from the database, so if I start an empty emulator instance, then several things will be missing from my app.

One thing I found that works wonder is exporting my online database and importing it in the emulator. For this, I use the node-firestore-import-export package.

It has excellent documentation, but right now, you only need a couple of things.

First, go into your Firebase console, and download your service account file, it's under Project Settings > Service Accounts > Generate new private key.

It will download a JSON file that you need to keep private since it's basically an admin key for your entire app.

Once you have the file, install the node export package, open your terminal and type:

npm install -g node-firestore-import-export

Then, to download your entire database, you can run this command:

firestore-export --accountCredentials serviceAccountFile.json --backupFile output.json

You'll need to replace serviceAccountFile.json for the actual name of the service account file you downloaded, and output.json for the name of the file you want.

Once downloaded, you can import it into the Firestore emulator. For that, you need to have the emulator running, open a separate terminal window, and use the import command.

There are two things to take into consideration before importing it. First, let's terminate the emulator instance in the terminal, and only start the firestore emulator:

firebase emulators:start --only firestore

We do this because if we import everything and we have cloud function listeners they will all trigger in that first import.

Then, we must tell the node package that we want to import to the emulator instead of the online database, for that, before running the import command, we must run in a different terminal window:

export FIRESTORE_EMULATOR_HOST=0.0.0.0:8081

That will make sure we write to the emulator and not the online database.

Remember, we're setting port 8081 there because that's the one that the CLI added for us in the configuration when we ran the emulator init command, if it created a different port for you, then use that one.

Now we can run the import command:

firestore-import --accountCredentials serviceAccountFile.json --backupFile output.json

Remember to change the name of the files according to how you downloaded them.

If you check the emulator UI now, your Firestore database should be populated. If not, and you see any issues, please let me know. I'm more than happy to help. 😊

Start your emulator with seed data

Once you added all the data to Firestore if you stop the emulator and start it again, it will start empty, so you'd have to run that set of commands every time.

Luckily, Firebase does have an option to back up our emulator, with the emulator running, and all the data already in Firestore, open a separate window and type the command:

firebase emulators:export ./emulators.backup

It will create a folder called /emulators.backup in our project (you can change the name to whatever you want), where all the data lives.

And once that's ready, whenever you're going to start the emulators, you're going to do it with this command:

firebase emulators:start --import=./emulators.backup

Changing the name of the file to whatever you used in the export.

That's it. Now you have a fully functional development environment that will let you both work faster and save money.

There is one drawback to this, and it's indexing, Firestore emulator will let every query go through, so you might be using range queries and things that require indexes, and the emulator will let you do it without the index, but then production app will break because of the missing index (Already happened to me 😜).

While the team decides how to solve this, the one thing that I found helped me, was using the new preview_channels for Firebase hosting, and deploy the app to a testing channel, click around, and make sure nothing was broken.

Do let me know if this was helpful or if there's something missing you'd like to know!


Become a Mobile Developer using JavaScript

Leave your email below and you'll get one or two emails a month sharing articles, videos, or courses that will help you jump-start your development with Ionic, Firebase, and Angular πŸš€.

    I don't do the spam thing, you can one-click unsubscribe at any time