3d cartoon hands holding a phone

Unlock full course by purchasing a membership

Lesson 4

Setting up a Development Environment for Firebase

EXTENDED

Setting up a Development Environment for Firebase

Since we will be integrating with Firebase for this application, there are a few more steps we need to complete in order to:

  • Set up local emulators so we don’t have to use real Firebase services during development
  • Install the firebase library
  • Install the rxfire library
  • Set up a Firebase project

We will be stepping through doing all of this in this lesson, and by the end of the lesson we will have a nice base to work from.

Install Firebase Tools

Install the firebase-tools package globally:

npm install -g firebase-tools

This will allow us to use the Firebase CLI to interact with various Firebase features. To begin, we are going to login with a Firebase account.

Run the following command:

firebase login

NOTE: If you do not already have a Firebase account you can create one here: firebase.google.com

NOTE: If you have used firebase recently and run into issues with authentication, you can try running firebase login --reauth

After authenticating with your Firebase account you will be able to do things like create/select projects, which will be important for the next step.

Installing RxFire

As well as using the Firebase JavaScript SDK in our project, we are also going to install rxfire which exposes some of the Firebase functionality as observables. This is going to make things easier for our purposes.

It is worth noting that it is possible to convert just about anything you need into observables using RxJS operators like from and fromEvent, but it is much nicer if we don’t have to handle those things ourselves.

Run the following command to install the Firebase SDK:

npm install firebase

NOTE: If you’ve installed any other dependencies with --legacy-peer-deps you will need to add --legacy-peer-deps every time you install.

Run the following command to install RxFire:

npm install rxfire

Update the moduleResolution in tsconfig.json:

    "moduleResolution": "node",

NOTE: This change is currently required to support rxfire

Configuration with the Firebase CLI

In order to generate a bunch of configuration we need for our project and also install the local emulators for Firebase services (like Authentication and Firestore databases) we are going to use the firebase init command.

Run the following command:

firebase init

Make the following selections:

  • Firestore
  • Emulators

When prompted choose Create a new project and follow the prompts to create your project.

This is going to error complaining that you haven’t set up Firestore yet and it will give you a link to do that. Follow that link and select Create database. Follow the prompts and when asked choose to:

  • Start in test mode

We will configure our security rules properly later. Once the database has been created we can retry initialising our project.

Run the following command (again):

firebase init

Make the following selections:

  • Firestore
  • Emulators

This time when prompted choose Use an existing project and select the one your just created.

Follow the prompts and accept the defaults for file names until you get to Emulators Setup.

When you get to the Emulators Setup stage make sure to choose:

  • Authentication Emulator
  • Firestore Emulator

Again, use the default values for the ports. When prompted, say yes to enabling the Emulator UI — this provides us with an interface similar to the Firebase console that we can access when running the emulators locally. Make sure you say yes to downloading the emulators.

At this point, you should have several new files added to your application:

  • .firebaserc
  • firebase.json
  • firestore.indexes.json
  • firestore.rules

Note that every time you update the security rules file, you will need to deploy it in order for those rules to go live. After making a change, you can either run:

firebase deploy

Which will deploy everything related to your Firebase project, or if you specifically want to deploy only the rules you can run:

firebase deploy --only firestore:rules

Enable Authentication

Head back to the Firebase console where we enabled the Firestore database before.

We will take the opportunity to enable Authentication as well now. Go to the Authentication (under Build as well) section from your Firebase project dashboard, choose Get started, and select the Email/Password option. Enable Email/Password and then click Save.

Configuring a Development/Production Environment for Firebase

Now we are going to have to do a little manual configuration ourselves. The goal here is that we want to:

  1. Use the emulators for Firestore and Authentication during development
  2. Use Firestore and Authentication from our real Firebase project during production

We also want this to happen automatically. We don’t want to have to remember to switch out configuration details when we are creating a production build or when we want to switch to development — this is a recipe for disaster.

Angular already has a built in mechanism for managing a development/production environment. You will find these two files in your project:

  • environment.ts
  • environment.prod.ts

First, let’s set up our production environment.

Modify the environments/environment.ts file to reflect the following:

export const environment = {
  firebase: {
    projectId: '******',
    appId: '***********',
    storageBucket: '**********',
    apiKey: '********',
    authDomain: '********',
    messagingSenderId: '****',
  },
  production: true,
  useEmulators: false,
};

Now you are going to need to replace these values with your own config for your project. To do this, you will need to go back to the Firebase dashboard again and add an app.

On the main dashboard you should see Add an app to get started. Select the Web option. Give a name to your app and click Register app.

You should now be given the values you need.

IMPORTANT: Do not copy and paste the entire snippet given to you by Firebase, only the values for:

  firebase: {
    projectId: '******',
    appId: '***********',
    storageBucket: '**********',
    apiKey: '********',
    authDomain: '********',
    messagingSenderId: '****',
  },

Use the values from your project to overwrite the values for the firebase option in the environment.ts file.

Notice that we have also added a useEmulators property — in a production environment we want that to be false. We are going to use this to enable/disable the emulators in our root module.

Modify the environments/environment.development.ts file to reflect the following:

export const environment = {
  firebase: {
    projectId: 'demo-project',
    appId: '******',
    storageBucket: '********',
    apiKey: '*****',
    authDomain: '*****',
    messagingSenderId: '********',
  },
  production: false,
  useEmulators: true,
};

Once again, replace with your own configuration but make sure to keep the projectId as demo-project for the development environment. When the emulators see a projectId that uses the demo-* naming convention, they will prevent access to any real Firebase services (which is what we want for development). Also note that useEmulators here is true and production is false.

Set Up App Config

Now we are going to have to do a little in-app configuration. We are going to place this configuration in our app.config.ts file.

Specifically, what we need to do in here is make a call to the initializeApp Firebase function (you may have noticed this earlier in the config Firebase gives you).

We will also be making some injection tokens again. Later, when we are using various functions from Firebase, we need to provide them with references to our Firestore and Authentication services.

However, when we are in development we want to configure these services to use the local emulators. When we are in production we want them to use the real services — a perfect use case for injection tokens! We can set them up such that we just inject them wherever we need them, and their values will just be configured automatically for whatever environment we are running in.

Modify your app.config.ts file to reflect the following:

import {
  ApplicationConfig,
  InjectionToken,
  provideZoneChangeDetection,
} from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

import { initializeApp } from 'firebase/app';
import {
  Firestore,
  initializeFirestore,
  connectFirestoreEmulator,
  getFirestore,
} from 'firebase/firestore';
import { getAuth, connectAuthEmulator } from 'firebase/auth';
import { environment } from '../environments/environment';

const app = initializeApp(environment.firebase);

export const AUTH = new InjectionToken('Firebase auth', {
  providedIn: 'root',
  factory: () => {
    const auth = getAuth();
    if (environment.useEmulators) {
      connectAuthEmulator(auth, 'http://localhost:9099', {
        disableWarnings: true,
      });
    }
    return auth;
  },
});

export const FIRESTORE = new InjectionToken('Firebase firestore', {
  providedIn: 'root',
  factory: () => {
    let firestore: Firestore;
    if (environment.useEmulators) {
      firestore = initializeFirestore(app, {});
      connectFirestoreEmulator(firestore, 'localhost', 8080);
    } else {
      firestore = getFirestore();
    }
    return firestore;
  },
});

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideAnimationsAsync(),
  ],
};

When providing Firestore and Auth to our application we are checking the useEmulators property we set up in our environment configurations. If useEmulators is set to true we connect to the emulators, otherwise we do not.

Using the Emulators

IMPORTANT: You will need Java installed on your machine in order to ues the emulators. Please check the documentation for information on required Java versions.

An important thing we need to keep in mind when using the emulators is that we actually need to have them running. To do that, we can run the following command:

firebase emulators:start

…and then in a separate terminal window run your normal application serve command. But, having to run two separate commands to run your application is a bit annoying, and I guarantee you will forget to start the emulators at least some of the time.

Instead, we can create a start script that we run to both start the emulators and serve our application. We will get to that in a moment, for now let’s just have a bit of a poke around.

Since we enabled the Emulator UI, after running the command to start the emulators you should be able to go to:

http://localhost:4000

to see the Firebase Emulator Suite dashboard. You might notice this looks somewhat similar to the normal Firebase dashboard. We can go into different services like Firestore and Authentication and interact with them in a similar way to using the standard dashboard.

Adding a Start Script

Now let’s set up that start script. First, stop the emulators by hitting Ctrl + C.

Modify the start script in package.json (under "scripts") to reflect the following:

"start": "firebase emulators:exec --project=demo-project --ui 'ng serve'",

NOTE: If you are using Windows you may need to set the command up like this instead:

"start": "firebase emulators:exec --project=demo-project --ui \"ng serve\""

The exec command will finish starting the emulators with the options we give it, and then once it is done it will run our command: ng serve. This way, all we need to do is run:

npm start

and it will both start the emulators and serve our application. Give that a go to make sure everything seems to be configured correctly so far, and then you can move on to the next lesson.