Set Up Development and Production Environment in Firebase

At the moment there is no out-of-the-box-like solution to tackle this. I suggest creating another Firebase project for development.

Once the project goes live, it is not suitable to test and develop in the same environment. Also, if the app uses 3rd-party authentication like Facebook, it might not possible to use the same 3rd party project for production and development.

At the moment there is no out-of-the-box-like solution to tackle this. I suggest creating another Firebase project for development. Let's call the production project todo, and the dev project todo-dev. This has some far-reaching consequences:

  • Database, Firestore, Authentication, and Storage for dev and prod are separated, what is exactly what we want.
  • Any changes made to one Firebase project must be introduced in the other to avoid hard-to-debug inconsistencies.

Practically this means that a change in a database security rule must be done in two places. Fortunately, there is a way to tackle the latter. To break down the process, we need to do three things:

  • Set up Firebase and 3rd-party project (Facebook in this case) for development.
  • Configure Firebase settings in the source code (firebase.json to be precise) to provide Firestore and Storage rules alongside Hosting and Functions settings. These will be the shared settings for both projects.
  • Set up a simple CD process so devs can never forget or make mistake in deploying their changes to the corresponding Firebase project.

Set Up Firebase and Facebook Project for Development

The first step is to create a brand new Firebase project. It has no connection to the production project (that is why we need to provide shared settings in later steps).

There is one thing that caused me some headaches: when I deployed to the new development project, I received the following error:

There was an issue deploying your functions. Verify that your project has a Google App Engine instance setup at https://console.cloud.google.com/appengine and try again. If this issue persists, please contact support.

I had to click through the Getting Started sections of the Firebase features I wanted to use. Apparently, some initialization processes need to run before the first deploy.

As my application used Facebook authentication, I had to create a separate app there too. In the Facebook dev dashboard, there is a way to create a test application. On developers.facebook.com, click on the project selector and select "Create New App". The process of setting up authentication for dev is the standard process of setting up a Facebook authentication for Firebase.

Configure firebase.json

I did not want to manually update Firestore and Storage rules or functions two places every time I make a minor change. Fortunately, firebase provides configuration options for this.

I could not find an official go-to documentation of all the possibilities in firebase.json, but there is a VS Code addon built by GitHub user SirWindfield, where he created a schema file for this. The main takeaway of this schema file is that all features of this JSON can be configured using project-specific settings, or using a default setting for all the available projects. In my case, there was no need at this point to make any differences between dev and prod, so with this JSON, they can now share the same settings.

{
    "hosting": {
        "public": "dist/todo",
        "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
        "rewrites": [
            {
                "source": "**",
                "destination": "/index.html"
            }
        ]
    },
    "firestore": {
        "rules": "firestore.rules"
    },
    "functions": {
        "predeploy": [
            "npm --prefix \"$RESOURCE_DIR\" run lint",
            "npm --prefix \"$RESOURCE_DIR\" run build"
        ]
    },
    "storage": {
        "rules": "storage.rules"
    }
}
firebase.json

What is new compared to a basic Firebase setup is the addition of firestore.rules and storage.rules. This contains nothing else but the rule code in the same way as they can be found in the Edit Rules tab of the Firebase UI.

Wire Up The Application

The last missing piece is wiring up the application to use the correct Firebase project.

import { NgModule } from "@angular/core";
import { AngularFireModule } from "@angular/fire";
import { AngularFireAnalyticsModule } from "@angular/fire/analytics";
import { AngularFirestoreModule } from "@angular/fire/firestore";
import { AngularFireFunctionsModule } from "@angular/fire/functions";
import { BrowserModule } from "@angular/platform-browser";
import "firebase/firestore";
import "firebase/functions";
import "firebase/storage";
import { environment } from "src/environments/environment";
import { AppComponent } from "./app.component";

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AngularFireModule.initializeApp(environment.firebaseConfig),
        AngularFirestoreModule.enablePersistence(),
        AngularFireAnalyticsModule,
        AngularFireFunctionsModule
    ],
    providers: [ ],
    bootstrap: [AppComponent],
})
export class AppModule {}
app.module.ts

In the environment.ts and environment.prod.ts, we should add the config object in our application's Project Settings page.

To deploy the Angular project, build the project with ng build --prod, then hit firebase deploy to deploy the application and all the Firebase related code.

What's Next

We have set up the development and production environment, and wired up in the Angular application.

However, we need to manually deploy to the development or production project every time there is a change. Firstly, we can easily forget to deploy. Secondly, we might accidentally deploy to the wrong project. We can utilize Github Actions to automate the process of deployment.