Deploy a monorepo React app on Firebase on multiple environments
Tags: firebase, javascript, react
Published: 2022-12-13 09:37:31

Here is how to host a JavaScript monorepo with multiple environments on Firebase.

Let's say you have a root project with packages that needs to be built and hosted. In our example, we have 3 React apps managed by the [lerna build system](https://lerna.js.org/). We have a front app, an admin app, and a design system app.

We need, at least, a staging and a production environment. We will use different Firebase projects for each environment, and within the projects different Firebase targets (i.e. apps or sites or subprojects) for each app of our monorepo.

So for each environment, we need to create a project with these steps :

1. Create a Firebase project, then initialize firebase using the `firebase init` command.

2. Deploy an empty file (such as a simple `index.html`) using the `firebase deploy` command. This step is important because it allows you to access targets (i.e. apps or sites or subprojects) in your Firebase project.

3. Create the Firebase apps (i.e. targets or sites or subprojects) for each app of our monorepos. You can do this by clicking "Add a site" in the Hosting Product on Firebase, or [via the firebase cli](https://firebase.google.com/docs/cli/targets) (not recommanded because it rewrite your `.firebaserc` file).

Now you have to modify the `firebase.json` file and the `.firebaserc` on your main repo, to configure for multiple targets on multiple projects.

4. Modify the `firebase.json` file to add the different targets for each app contained in the monorepo.

5. Deploy the apps using the `firebase deploy -P <project_name>` commands for each environment. So if our projects are `prod` and `staging` you deploy with `firebase deploy -P staging` and `firebase deploy -p prod`.

Here is my `firebase.json` file. In this one, you specify the targets (subprojects) with how they are hosted (with the `build` folder)

```json
{
  "hosting": [{
    "target": "app1",
    "public": "packages/app1/build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  {
    "target": "app2",
    "public": "packages/app2/build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  },
  {
    "target": "app2",
    "public": "packages/app3/build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }]
}

```

And here is my `.firebaserc` file. In this one you specify the Firebase projects for each environment and on which sites (i.e. subprojects) the targets must be hosted.

```json
{
  "projects": {
    "default": "myproject-staging",
    "staging": "myproject-staging",
    "prod": "myproject-prod"
  },
  "targets": {
    "myproject-staging": {
      "hosting": {
        "app1": [
          "myproject-app1-staging"
        ],
        "app2": [
          "myproject-app2-staging"
        ],
        "app3": [
          "myproject-app3-staging"
        ]
      }
    },
    "myproject-prod": {
      "hosting": {
        "app1": [
          "myproject-app1"
        ],
        "app2": [
          "myproject-app2"
        ],
        "app3": [
          "myproject-app3"
        ]
      }
    }
  }
}

```

To integrate this into a CI/CD strategy, you can use GitLab and add command lines to the deployment script to perform the deployments on Firebase.

To obtain the authentication token, you can use the `firebase login:ci` command (although this method is deprecated, it is recommended to check for current alternatives to obtain the authentication token).

Here is my `gitlab-ci.yml` file to build and deploy on firebase each environment when they have new commits on the `develop` and `main` branches (for `staging` and `production` environments)

```yaml
image: node:18.12.0

stages:
  - deploy

variables:
  REACT_APP_API_URL: <API_URL_STAGING>
  REACT_APP_SENTRY_ENVIRONMENT: staging

deploy-staging:
  stage: deploy
  only:
    - develop
  environment: staging
  variables:
    REACT_APP_API_URL: <API_URL_STAGING>
    REACT_APP_SENTRY_ENVIRONMENT: staging
  before_script:
    - GENERATE_SOURCEMAP=false
  script:
    - npm install -g firebase-tools
    - yarn --ignore-platform
    - NODE_OPTIONS=--openssl-legacy-provider yarn run build
    - firebase deploy -P staging --token $FIREBASE_TOKEN

deploy-production:
  stage: deploy
  only:
    - main
  environment: production
  variables:
    REACT_APP_API_URL: <API_URL_PRODUCTION>
    REACT_APP_SENTRY_ENVIRONMENT: production
  before_script:
    - GENERATE_SOURCEMAP=false
  script:
    - npm install -g firebase-tools
    - yarn --ignore-platform
    - NODE_OPTIONS=--openssl-legacy-provider yarn run build
    - firebase deploy -P prod --token $FIREBASE_TOKEN
```

I hope you find this usefull. I got stuck for a couple of hours because when I wanted to host the staging environment, the project was not initiated. I had to erase my `firebase.json` file, recreate one with `firebase init` to host the project with a empty `index.html` file. Only then I could create the apps (subprojects = targets) in Firebase and then use my multitargets `firebase.json` file.