Creating An AppConfig Service In Angular

In almost all of my Angular projects, one of the first things I add is a simple AppConfig service that allows me to swap out configurations on the fly for different environments. In most cases, this is so that I can set a different base API endpoint for my different environments, and have them swapped in at release time. Let’s jump into it!

Why Not Just Use Angular Environments?

So Angular actually has an inbuilt concept of “environments” (more info here) that is pretty similar to a typical appconfig setup. But the main issue is that this environment is also tied to individual builds. In modern day scenarios we want to build *once* and release multiple times. If our app settings are swapped out/configured at build time, this doesn’t afford us the ability to only build once and deploy multiple times. Instead we are building a different distribution for each environment.

Instead, I would highly recommend keeping Angular Environments for settings that you want to have at build time (e.g. Turn off Debug), rather than setting up individual settings between Dev,  Test,  Production etc.

App Config Is Not A Secret Store

The next thing I want to touch on is that no matter what we do in Angular, it’s still a front end web technology. That means that, even if we obfuscate things a bit, any user can still view the source code. In this tutorial, we are using a publicly available JSON file as our application configuration file, so even more so, it’s not a secret store. Do not put anything in there that you don’t want shared to the public.

Really, in most cases we are just going to be putting in there a API base endpoint and maybe a couple of other small config params that change markup. But nothing like like DB Connection Strings, authentication tokens etc.

Create Our Config Files

The first thing to do is to create a couple of configuration files in a safe directory. By safe I mean that Angular will just take the directory as a whole and output it when building. We don’t want Angular to do anything with our config files, just copy them when we build.

For that reason I typically choose the assets  folder which is created when you create a new Angular project from the CLI.

So in a folder at /assets/config  I create two files. The first is just called app-settings.json with the following :

{
    "apiBaseUrl": "https://localhost:5001"
}

And then I create another called app-settings.production.json with the following :

{
    "apiBaseUrl": "https://myapi.com"
}

So that’s our config files ready to go!

Creating The AppConfig Service

Next create a new service using the Angular CLI called AppConfig. Then replace all the code with the following :

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class AppConfigService {

  private appConfig: any;
  private http : HttpClient;
  
  constructor(http: HttpClient) {
	this.http = http;
  }

  loadAppConfig() {
    return this.http.get('/assets/config/app-settings.json')
      .toPromise()
      .then(config => {
        this.appConfig = config;
      });
  }

  get apiBaseUrl() : string {
    return this.appConfig.apiBaseUrl;
  }
}

Let’s walk through this a little, even though it’s pretty simple.

We have a method called loadAppConfig that essentially calls our config files we created earlier, then loads them into a local appConfig object that is basically a dynamic type.

We then have a property called apiBaseUrl that will return the apiBaseUrl that we just retrieved from our json file.

But there’s a problem. Before we can retrieve the apiBaseUrl, we first need to call loadAppConfig. And we only really want to do this a single time to load the file into memory, after that any further calls to load the json file are a complete waste. We could write some code in here with a boolean to check if we have loaded the config already, and if we haven’t to load it, and then continue. But that’s naff.

Luckily Angular already has a feature to help us!

Using APP_INITIALIZER To Load Configuration

In a previous post we talked about how to use APP_INITIALIZER in Angular to do a “one time” activity when the Angular app bootstraps for the first time. If you haven’t come across this feature before, I highly recommend going to read it because the following might not make much sense otherwise : Using The APP_INITIALIZER Token To Bootstrap Your Application. 

All we need to do is head to our main app.module.ts, and add a line to our providers array that looks like so :

providers: [
	{ 
	  provide : APP_INITIALIZER, 
		multi : true, 
		 deps : [AppConfigService], 
		 useFactory : (appConfigService : AppConfigService) =>  () => appConfigService.loadAppConfig()
	}
]

What this does is say, when our app is started, can you please call the following method. Our loadAppConfig method actually returns a promise anyway, but our application will wait until this promise completes before continuing on.

It may seem backward to wait until our configuration file loads to continue loading our application. Won’t the startup time be noticeable? Maybe. But we also can’t be sure what will be calling the config service and, more importantly, when. So it’s better for us to just load everything up front and be ready when things hit us.

Swapping Configurations At Release

Now comes the easy, but… different… part. By different I mean this next step will totally depend on how you release your application. The nuts and bolts of it is that when you release to say, production, you will need to rename the file app-settings.production.json to app-settings.json. That way when your service loads your configuration file, it’s the right one for that specific environment.

For me, because I’m doing releases from Azure Devops, I wrote a quick piece of powershell to simply rename the file. But you can use any sort of script, or maybe even an inbuilt step in your release pipeline can do it. Either way, renaming the file is the last step and you are ready to go with your brand new app config service in Angular!

Leave a Reply

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