It is common for developers to setup multiple environments for our application. This usually include a development/staging and production environment. This gives us the freedom to experiment with our development/staging environment, without messing with the production environment. Only after thorough testing do we deploy to production.

On top of that, the staging environment can be used to demonstrate and test new features before deploying them to production. Think of it like the Windows Insider Program, where testers (Insiders) get new features beforehand to test and provide feedback. For instance, you can push changes to a staging environment to show your client new features. This enables you to collect their feedback and approval before deployment to production.

We are going to use multiple firebase projects for a multi-environment setup. This is because we want to isolate everything i.e. functions, cloud Firestore etc. in each environment. Ensuring that changes we make on development/staging don’t affect the production environment – or in this case firebase project.

This way, we can develop our application while knowing that all the changes we make will not break our production application. Then, once satisfied with our work, we can safely deploy to our production application.

NB: We are going to be using Angular, but with some minor changes, this will work with any client app – frontend or mobile.

Getting Started

We are going to start by creating two new firebase projects. One for production and the other for both development and staging purposes. We are using one for both development and staging to keep things simple. If you already have an existing project, you can just add another, with an appropriate name.

Once you have created your projects, copy the configs for web from both of your firebase projects. They should look like this:

var config = {
  apiKey: '<API_KEY>',
  authDomain: '<PROJECT_ID>.firebaseapp.com',
  databaseURL: 'https://<DATABASE_NAME>.firebaseio.com',
  projectId: '<PROJECT_ID>',
  storageBucket: '<BUCKET>.appspot.com',
  messagingSenderId: '<SENDER_ID>'
};

Next, inside our Angular project, we need to add the above firebase configs, to our angular environment files. You can learn more about configuring Angular deployments environments here. But in a nutshell, by default, angular provides two environment files, located inside the environment directory under the src directory of your angular project. During compilation, Angular will replace the default environment file with the production, when the --prod flag is used.

Add the configurations from your dev/staging firebase project to environment.ts. And those from you production firebase project to environment.prod.ts:

// environment.ts
// configurations for dev/staging firebase projects

export const environment = {
  production: false,
  firebase: {
    apiKey: '<API_KEY>',
    authDomain: '<PROJECT_ID>.firebaseapp.com',
    databaseURL: 'https://<DATABASE_NAME>.firebaseio.com',
    projectId: '<PROJECT_ID>',
    storageBucket: '<BUCKET>.appspot.com',
    messagingSenderId: '<SENDER_ID>'
  }
};
// environment.prod.ts
// configurations for production firebase projects

export const environment = {
  production: true,
  firebase: {
    apiKey: '<API_KEY>',
    authDomain: '<PROJECT_ID>.firebaseapp.com',
    databaseURL: 'https://<DATABASE_NAME>.firebaseio.com',
    projectId: '<PROJECT_ID>',
    storageBucket: '<BUCKET>.appspot.com',
    messagingSenderId: '<SENDER_ID>'
  }
};

Next, go ahead and setup your angular project with firebase using AngularFire2.

First install both AngularFire2 and Firebase into your Angular project if you haven’t yet.

$ npm install @angular/fire firebase
$ yarn add @angular/fire firebase

And then import AngularFireModule in your app module. (If you are using lazy loading, import it to a feature/shared module instead).

// imports
@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase)
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

Next, let’s setup firebase inside our project so that we can use both of our projects together.

Setting up Firebase for our Project

First, we are going to initialize firebase project. Make sure you have Firebase CLI installed globally. If you haven’t, you can do so by following the instructions here.

Then, initialize firebase for the Angular project by running firebase init at the root of our angular workspace.

$ firebase init

When prompted for the firebase project to use, select the development/staging one. Then, answer the next prompts that follow up appropriately. The project that you selected above is known has an alias of default. This will become clear once we add a second firebase project.

Then, we need to add a second project, on top of the project which we added during initialization. Fortunately, Firebase CLI provides a way of doing that, using the firebase use commands. We can add a second firebase project by using the firebase use --add.

$ firebase use --add

When you run the above command, Firebase CLI will prompt you to select a second project. And then will prompt you to enter an ALIAS for the project. Select the project you created for production purpose and use prod as the ALIAS of the second project.

Please note that we have two firebase projects setup, one known as the default, for dev/staging and the other one prod for production. If you ran firebase deploy at this point, firebase will deploy to the default firebase project. To deploy to the production firebase project, there are two methods:

The first method is to use the firebase use <alias> command to switch the firebase project to deploy to, followed by the firebase deploy command:

$ firebase use default

$ firebase use prod

And the second method is to use the -P flag to specify the firebase project to deploy to when deploying:

$ firebase deploy -P default

$ firebase deploy -P prod

NB: If you forget to specify the -P flag, firebase CLI will deploy to the firebase project set using firebase use <alias> command.

With that, you should be able to easily deploy your app to the appropriate firebase project, giving you a nice separation of deployment environments.

What about Firebase Functions?

If you are using callable functions, no modifications are necessary as they are dictated by the firebase configs for your project. But, if you are using http firebase functions, the http endpoint for the functions is going to vary based on the firebase project you are targeting.

The good thing is you can solve this by using angular environment variables. You can add the functions http endpoint for each firebase project, inside the appropriate environment file. I.e. For production firebase function, add the http endpoint to the environment.prod.ts and the same goes for dev/staging.

// other environment variabes
firebase: {
  // other firebase configs
  functions: {
    Function1: 'Function EndPoint';
  }
}

And then, you can use the above firebase function end point as follows:

const url = environment.firebase.functions.Function1;

Hosting Multiple Sites Per Project

You might also want to use multi-site hosting in Firebase. This is quite useful if you wanted to have a client and admin site for your app. The best way to achieve this in Angular is using Angular Workspaces, which enables you to have more than one app in your angular project. You can learn more about Angular workspaces here.

Assuming you have your second site ready, go ahead and add a second hosting site for each of your firebase projects – dev/staging and production. You can follow the instructions here which explain everything in details. Please note that you will have to switch to a paid firebase plan for you to be able to do this, I suggest using pay as you go. Proceed at your own discretion.

Please note that, each firebase site is unique across all firebase account, since the site subdomain, is accessible over the internet. As such, the name of your sites can not be re-used across your firebase projects. Therefore, to identify which site to deploy to, Firebase CLI uses targets, which are a lot like aliases for firebase projects above.

By using targets, we can deploy to the correct site on firebase hosting despite their unique name, in each of our firebase project. First, switch to the firebase project you want to setup, using the firebase use <alias> command.

And then, use the command below, to link a target name to a site name. The site name is the subdomain of the site, a unique identify across all firebase projects.

$ firebase target:apply hosting target-name site-name

For instance, if you had a client and admin sites, then it would look something like this:

$ firebase use default

Then:

$ firebase target:apply hosting client client-dev-site-name

$ firebase target:apply hosting admin admin-dev-site-name

You will also have to do the same for the second firebase project, switch to it and repeat the above process. Ensuring that you change the name of the site appropriately.

$ firebase use prod

Then:

$ firebase target:apply hosting client client-prod-site-name

$ firebase target:apply hosting admin admin-prod-site-name

NB: As of the writing this post, site and target names with hyphen (-) case do not work as expected, please avoid that for now.

In a nutshell, we are telling Firebase CLI to use targets client and admin, to target the corresponding site inside the firebase project we are using. For instance, when deploying to production, Firebase CLI will use the target to identify the subdomain of the site to deploy to. The same also applies when deploying to dev/staging firebase project.

You can now deploy your angular app as shown earlier, by using the -P flag or switching between the firebase projects:

$ firebase deploy -P default

$ firebase deploy -P prod

or

$ firebase use default

$ firebase deploy

$ firebase use prod

$ firebase deploy

NB: You can use all the firebase deploy flags like --only or --except as you normally would in any other firebase project.

TIP

You can use NPM scripts to deploy to the appropriate firebase project smoothly.

// package.json
"scripts": {
  // ...
  "deploy:dev": "firebase use staging && ng build --aot && firebase deploy",
  "deploy:prod": "firebase use default && ng build --prod && firebase deploy"
},

Each of the above script will change to the appropriate firebase project, build your angular app and deploy it, alongside all other firebase services you have setup.

And then you can easily deploy using the following command:

For production:

$ npm run deploy:prod

For development/staging:

$ npm run deploy:dev