Handling Environment Variables in Angular Application – RuntimeEnv, @Inject

This article shows how to make the angular code to be automatically reliant on the variable changes based on the environment it is deployed to.

Certain data will need to change specific to the environment the application is currently deployed to. The best practice is to make the development team independent of this and have the CI/CD accountable for using the right data in right place.

Introduction

Env variables are used mainly for the following reasons:

  1. To avoid the secrets getting checked into code
  2. Make it easy to change values during CI/CD deployments to different environments. Like Dev, QA, Staging, Prod.

When it comes to front end technologies, the browser renders static files. Either the client side has the code and just renders based on data from server (single page apps) or the server creates static pages based on user requests and sends to browser for rendering. 

Either way, the browser will expose the secrets. So the secrets can’t be secret on client-side code. However, they can be made sure not to check-in client secrets into the code/repository. 

CI/CD makes sure deployments of the same code will be done on different environments like Dev/QA based on parameters we pass. On that note, front-end static code might have content/ids that need to be different for Dev/QA.

Use-Cases 

  1. When using authentication libraries like auth0, okta, Cognito,…
  2. Using libraries with needs licenses or API key to function. 

How can this be done in an elegant way? – Solution

During the deployment process, env_vars.js the file will have to be replaced with the values corresponding to the application environment. We will be coding the Angular app in such a way that every time it renders on the browser, values will be fetched from the file(env_vars.js) during runtime.

Here are the stepwise instructions to achieve the variable injection into Angular Module

Step 1

Create a file env_vars.js under src folder of your project

env_vars.js

var environment = {
    ENV_VARIABLE_1: '',
    ENV_VARIABLE_2: '',
    ENV_VARIABLE_3: ''
};

Step 2

Add env_vars.js to assets path in angular.json configuration file. This is done to include env_vars.js file on the build output folder (dist folder).

"build": {
    ... // other config
    "options": {
        ... // other options
        "assets": [
            "src/favicon.ico",
            "src/assets",
            "src/env_vars.js" // added here
        ],
         
    },
}

Step 3

Create an Interface to contain the ENV variables you’ll be mapping in the application.

src/config/env-config.interface.ts

export interface IEnvConfig {
    ENV_VARIABLE_1: string,
    ENV_VARIABLE_2: string,
    ENV_VARIABLE_3: string
}

Step 4

Create a module to fetch the values from env_vars file during browser runtime and provide into the angular module.

Values in env_vars file can be access as shown below:

export const runtimeEnv: IEnvConfig = (<any>window).environment || {};

const envValue = {
    env1: runtimeEnv.ENV_VARIABLE_1
};

// or

const env2 = runtimeEnv.ENV_VARIABLE_2

Below is an real time example to initialise OKTA Config using runtime environment

src/config/env-config.module.ts

import { NgModule } from "@angular/core";
import { IEnvConfig } from "./env-config.interface";
import { OKTA_CONFIG } from "@okta/okta-angular";

export const runtimeEnv: IEnvConfig = (<any>window).environment || {};

const oktaConfig = {
    issuer: runtimeEnv.ENV_VARIABLE_1,
    redirectUri: runtimeEnv.ENV_VARIABLE_2,
    clientId: runtimeEnv.ENV_VARIABLE_3,
    pkce: true
  }

@NgModule({
    providers: [
    {provide: OKTA_CONFIG, useValue: oktaConfig}
    ]
})

export class EnvConfigModule {

}

Step 5

Finally, the EnvConfigModule needs to be imported in the App Module.

src/app/app.module.ts

@NgModule({
    ...
    imports: [
      ...,
      EnvConfigModule
    ]
})

Now, what if those variables/values need to be accessed within a component?

Simple, Angular provides an elegant way of doing it using injection. Following are the additional steps to be followed to access ENV values in components/services

1. Create an data service where an injection token will be used

src/services/data.service.ts

export const ENV_VARIABLE_1 = new InjectionToken("ENV Variable 1");

2. In the EnvConfigModule, provide the value to the injection token variable created in the previous step

import { NgModule } from "@angular/core";
import { IEnvConfig } from "./env-config.interface";
import { ENV_VARIABLE_1 } from "../services/data.service";

export const runtimeEnv: IEnvConfig = (<any>window).environment || {};


@NgModule({
    providers: [
    {
       provide: ENV_VARIABLE_1, 
       useValue: runtimeEnv.ENV_VARIABLE_1
     }
    ]
})

export class EnvConfigModule {

}

3. Now, this can be accessed in components/services as shown below,

constructor(
    @Inject(ENV_VARIABLE_1) private env1: string
    ) { }

It can be accessed in methods as this.env1


To conclude, the above method is a neat implementation of handling environment variables in Angular front end code. This approach gives the flexibility to add additional Environment variables as the application scales up without any major code/logic changes.

Thank You!

Clap

Leave A Comment