Create Firebase Hosting / Function Staging and Production in Ionic App

Firebase Hosting Staging and Production

Terminal

Syntax

firebase target:apply hosting <targetName> <resourceName>

Sample

firebase target:apply hosting staging firebase-project-id-staging

How to get the "Resource" name

Make sure to set up the Hosting configuration inside the Firebase console first and use the domain name (remove the "web.app")

How to add the "Target" in firebase target:apply hosting staging firebase-project-id-staging

  • This might cause this error is not set up properly: Must supply either "site" or "target" in each "hosting" config.
  • Add target object inside hosting array. Make sure to have different public folder for production (app) and staging(staging)


firebase.json

{
  "hosting": [
    {
      "public": "www",
      "ignore": [
        "firebase.json",
        "**/.*",
        "**/node_modules/**"
      ]
    },
    {
      "target": "production",
      "public": "www",
      "ignore": [
        "firebase.json",
        "**/.*",
        "**/node_modules/**"
      ],
      "rewrites": [
        {
          "source": "**",
          "destination": "/index.html"
        }
      ]
    },
    {
      "target": "staging",
      "public": "www-dev",
      "ignore": [
        "firebase.json",
        "**/.*",
        "**/node_modules/**"
      ],
      "rewrites": [
        {
          "source": "**",
          "destination": "/index.html"
        }
      ]
    }
  ],
}

Execute command firebase target:apply hosting staging firebase-project-id-staging

.firebaserc (Before the target command)

{
  "targets": {
    "firebase-project-id": {
      "hosting": {
        "app": [
          "firebase-project-id"
        ]
      }
    }
  },
  "projects": {
    "default": "firebase-project-id",
  }
}

.firebaserc (After the target command)

{
  "targets": {
    "firebase-project-id": {
      "hosting": {
        "app": [
          "firebase-project-id"
        ]
      }
    },
    "firebase-project-id-staging": {
      "hosting": {
        "staging": [
          "firebase-project-id-staging"
        ]
      }
    }
  },
  "projects": {
    "default": "firebase-project-id-pwa",
    "staging": "firebase-project-id-staging"
  }
}

Transition to different development

Syntax

firebase use <projectName>

Where to find the

.firebaserc

{
  "targets": {
    "firebase-project-id": {
      "hosting": {
        "app": [
          "firebase-project-id"
        ]
      }
    },
    "firebase-project-id-staging": {
      "hosting": {
        "staging": [
          "firebase-project-id-staging"
        ]
      }
    }
  },
  // Use the projects property as <projectName>
  "projects": { 
    "default": "firebase-project-id-pwa",
    "staging": "firebase-project-id-staging"
  }
}

Implementation

firebase use default // Production
firebase use staging // Staging

Project build

ionic build --prod // Production
ionic build -c=staging // Staging

Alternative

ng build --configuration=production // Production
ng build --configuration=staging // Staging

Note: You can use the alternative command when you set up this properly in angular.json file. Add *staging object under the configurations


angular.json

{
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "serviceWorker": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "50kb"
                }
              ],
              "ngswConfigPath": "ngsw-config.json"
            },
            "staging": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.prod.ts",
                  "with": "src/environments/environment.ts"
                }
              ],
              "outputPath": "www-dev"
            },
          }
}

Firebase deploy to Hosting

firebase deploy --only hosting:default // Production
firebase deploy --only hosting:staging // Staging

or Short method

firebase deploy -P default  // Production
firebase deploy -P staging  // Staging

Alternative

// Production
firebase use default
firebase deploy --only hosting

// Staging
firebase use staging
firebase deploy--only hosting

Note: You can also use firebase deploy after firebase use if you only set up hosting in the firebase connection


Firebase Function Staging and Production

First go the environment you need to set up

Terminal

firebase use default // Production
firebase use staging // Staging

Set up the dynamic credentials.

Use case: Different Firebase hosting or API connections for staging and production environment that shares one firebase function


Terminal

firebase functions:config:set creds.api="5b3323d" creds.storagebucket="firebase-staging.appspot.com"

Firebase function sample (index.js)

Add functions.config() to use the credential configuration

exports.getAPI = functions.https.onRequest((req, res) => {
  const headers =   {
            'API-Id': functions.config().creds.api,
            'content-type': 'application/json'
        };
  console.log(headers)
})

To check the credential that have been set up for the current environment


Terminal

firebase functions:config:get

Output

{
 creds: {
  api: '5b3323d',
  storageBucket: 'firebase-staging.appspot.com'
 }
}

Update the configuration

firebase functions:config:set creds.api="10101" creds.newkey="12345" keypath.url="./firebase-staging.json"

Output (Previous set up)

{
 creds: {
  api: '5b3323d',
  storageBucket: 'firebase-staging.appspot.com'
 }
}

Output (New)

{
 creds: {
  api: '10101',
  storageBucket: 'firebase-staging.appspot.com',
  newkey: '12345'
 },
 keypath: {
  url: './firebase-staging.json'
 }
}

How to have dynamic Firebase admin credential key

  1. Download first the credential json file from Firebase console Settings > Service Accounts and click Generate new private key button located at the button of the page
  2. Add the downloaded file inside the function folder (firebase function directory in the local)
  3. Update the require command inside the firebase function index.js to const key = require('' + functions.config().creds.keypath); . Make use to concat the functions.config to blank string.

index.js

const functions = require('firebase-functions');
const key = require('' + functions.config().creds.keypath);
const fbAdmin = require('firebase-admin')

fbAdmin.initializeApp({
    credential: fbAdmin.credential.cert(key),
});

After firebase functions:config:set, deploy it using

firebase deploy --only functions

Ionic Staging and Production

Set up the production and staging inside the angular.json. Make sure to add fileReplacements and outputPath property

{
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "outputPath": "www"
            },
            "staging": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.prod.ts",
                  "with": "src/environments/environment.ts"
                }
              ],
              "outputPath": "www-dev"
            },
          }
}

Note: This works well when you try to deploy this to different Firebase hosting for production and staging but it won't work well in the localhost.

Serve based on environments in Ionic localhost

If you use ionic serve for local development. It doesn't implement the fileReplacements for environment.ts.

Solution:

ng serve --configuration=production // Production
ng serve --configuration=staging // Staging

References:

https://firebase.google.com/docs/hosting/multisites https://firebase.google.com/docs/cli/targets https://github.com/firebase/firebase-tools/issues/902 https://jsmobiledev.com/article/multiple-firebase-projects