A Better Approach to Environment Variables in Angular

Angular provides a default way to configure environment variables, which you can learn more about here. But in a nutshell, you have different environment files, where you store environment variables and during the build process, angular compiler will use the correct environment file to replace and the default environment variables.

So, if you are targeting production environment, angular will replace the default environment file with the production when you use the –prod flag. This works but has some shortcoming. Like for instance to change any of those configurations, you must rebuild and deploy the whole application. Would it be better if you could change some variables, such as API Keys dynamically without need to go through that process?

A Better Approach

The approach is rather simple, and it involves storing the data on the backend on an environment folder. Then during app initial stage or even initialization, you can load the configuration file (JSON) using a service. In this scenario, we will have different JSON files for different environments. We will then let the app load the appropriate configuration based on the environment it has been deployed in.

To distinguish between different environment configurations, we are going to add a new environment name property under each of environment. If we have three environments, then we are going to have 3 environments files: environment.ts, environment.prod.ts and environment.staging.ts for each of our environments.

export const environment = {
  production: true,
  wpEndpoint: 'Some Endpoint',
  environment: 'staging', // environment name property
  ...
};

The same needs to be done for each of our other environment names:

// Production
...
environment: 'staging', // environment name property
...
// Development
...
environment: 'development', // environment name property
...

Now, we can call a http service to retrieve the json files containing  that environments configurations, which we are storing in a directory on a server or AWS S3 bucket.

NB: Any sensitive API Keys should not be stored inside the frontend app. Instead have your backend call those APIs on behalf of the frontend. This ensures they remain private, out of reach from the public.

Loading the Configurations

With the environment name property, you can stitch together the URL to the correct configurations file.

const url = 'some-server-url' + environment.environment + '.json';

And then you can call the API Endpoint as shown below:

this.http.get(url).pipe(
  map((res: Response) => {
    return res.body;
  })
);

NB: You can strongly type cast the results. This allows you to have easy access to the variables inside the JSON File just like a normal environment file. Allowing for intellisense when using  Angular Language Service.

Now anytime you want to change configurations, you simply edit the JSON file containing the configuration of your environment. No need to build and deploy a new version of your application just for simple change.

When to fetch this configuration is up to you. You can load them in the first component, just before anything else. Another option is during app initialization, just before the app has started off. I prefer the last option as it means the configurations will be available to the app as when starts. You can learn more about this here.

Caching

While storing some of the configurations on the backend is a good idea. On the hand, retrieving them each time the app is loaded or reloaded is not a very good idea. You will need to implement some sort of local caching, I suggest using a Service Worker.

A local cache will do two things, first store a local record of the configuration for a specified period – an hour, 30 minutes etc. And secondly speed up your app as it eliminates network latency required while fetching the configurations.

The good thing about using a service worker is you can use performance configuration. This basically means that, it will only check for fresh content from the server after the specified cache period has expired. You can also use freshness settings, in this case it just checks whether there is new content and if not just loads from local cache. You can learn more about Service Workers here.

And all without the need to implement your own caching system, just a few configurations. The only downside is browser support, which you can learn more about here.

Final Thoughts

This method is not perfect, but it provides a much better approach to environment variables. Of course, not every app is going to benefit from this and for a lot of apps out there, the default approach is still a better alternative. It all depends with what your application does, if it relies with a lot of API keys to third party service, then dynamically changing them is not a bad idea. If your environment variables rarely change, and when they do change have to be tested before deployment, then the default approach works just fine.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.