DEV Community

Tom Bloom
Tom Bloom

Posted on • Edited on

Deploy a monorepo React app on Firebase on multiple environments

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. 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 (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.

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

  2. 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)

{
  "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/**"
    ]
  }]
}

Enter fullscreen mode Exit fullscreen mode

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.

{
  "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"
        ]
      }
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

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)

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
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)