Angular Runtime Configuration

Most apps need some sort of Run-time configuration information, which it needs to load at startup. For example, if your app requires data, then it needs to know the base location of your REST endpoints. Also, development, testing & production environments may have different endpoints. This tutorial covers how to read the config file in angular using the APP_INITIALIZER

Where to Store configuration

The Angular has the environment variables where you can keep the runtime settings, but it has limitations. The setting of the environment variables are defined at build time and cannot be changed at run time.

We can keep the configuration information in a database. But we still need to store the REST endpoints to connect to the database.

The right approach is to store the configuration information in a config file in a secured location. We will deploy the configuration file along with the App. The App can load the configuration from it when the application loads.

For the examples in this tutorial, we will keep it in the src/app/assets/config folder

You can use any format to store the configuration. The popular choice is either JSON or XML format. For Example appConfig.json or appConfig.xml

Below is the typical structure of the configuration file (or config file) in JSON format.

When to read the configuration

Some of the configuration information is needed before we load our first page. Hence it is better to read the configuration very early in the application. The Angular provides the injection token named APP_INITIALIZER, which it executes when the application starts.

APP_INITIALIZER

The APP_INITIALIZER is the predefined injection token provided by Angular. The Angular will execute the function provided by this token when the application loads. If the function returns the promise, then the angular will wait until the promise is resolved. This will make it an ideal place to read the configuration and also to perform some initialization logic before the application is initialized.

To use APP_INITIALIZER first we need to import it our Root Module.

Next, We need to create a service, which is responsible for reading the configuration file. The AppConfigService in the example below loads the configuration in its load method

Next, create a factory method, which calls the load method of AppConfigService. We need to inject the appConfigService into the factory method as shown below

Finally, use the APP_INITIALIZER token to provide the initializeApp using the useFactory. Remember to use the deps to add AppConfigService as a dependency as the initializeApp uses that service. The multi: true allows us to add more than one provider to the APP_INITIALIZER token.

Suggested Reading

Reading the Configuration file

To Read the Config or Configuration file, we need to make an HTTP GET request and return a Promise.

If you do not return a promise, then angular will not wait for the function to finish. The observable is not yet supported

Example Application

Create a new Angular App.

Create the Config file

We will use the JSON format for our configuration.

First, We will create an Interface IAppConfig

Create the app-config.service.ts in the src/app folder and create IAppConfig as shown below.

Then create the actual configuration file in the assets/config/config.json as shown below

Service

The task of the service is to send the HTTP GET request to the config.json file and store the configuration.

Create static settings variable

Next, we inject HttpClient in the constructor. We use the HTTP get method to read the configuration file.

In the load method, jsonFile constant is assigned to the location of the config file.

Then, we return the Promise

Inside the Promise we make a GET request to the config file. The returned response is mapped to the IAppConfig interface.

Assign it to the settings variable. Note that it is a static variable. Hence, we are using AppConfigService.settings here.

Output the values to the console

And Finally, call the resolve to return the Promise

And, in case of any errors catch it and reject the Promise. The Angular will stop loading the application

Loading the Runtime configuration

Next, step is to inject the Service in AppModule

First, we need to import APP_INITIALIZER from the @angular/core.

Next import AppConfigService & HttpClientModule

We have appConfigService which loads the configuration. Now we need a function, which invokes the load method. Hence we will create a function initializeApp, which calls the appConfigService.load() method

Finally, we need to tell angular to execute the initializeApp on application startup. We do that by adding it to the providers array using the APP_INITIALIZER token as shown below.

The useFactory is used because initializeApp is a function and not a class

We make use of the deps:[AppConfigService] flag to let angular know that initializeApp has a dependency on AppConfigService.

The multi : true creates the multi-provider DI token. The APP_INITIALIZER is a multi-provider token. We can define more than one Provider for APP_INITIALIZER. The Angular Injector invokes each of them in the order they appear in the Providers array.

Read the configuration in components

The following AboutUsComponent shows how to read the runtime settings in the component

First import the AppConfigService in the component/service.

Next, get a reference to the AppConfigService.settings

And the use it in your component/service etc

Summary

In this article, we learned how to create and runtime configuration and use it in an angular application. We use the injection token APP_INITIALIZER provided by Angular to hook into the Angular initialization process and read the config file. The config file is in JSON format and stored in the src/app/assets/config folder. We make an HTTP GET request to get the data from the config file.

Source Code

Download Source Code

8 thoughts on “Angular Runtime Configuration”

  1. Hy
    Same problem for me:
    Error: src/app/service/app-config.service.ts:16:54 – error TS2345: Argument of type ‘(response: IAppConfig) => void’ is not assignable to parameter of type ‘(value: Object) => void | PromiseLike’.
    Types of parameters ‘response’ and ‘value’ are incompatible.
    The ‘Object’ type is assignable to very few other types. Did you mean to use the ‘any’ type instead?
    Type ‘Object’ is missing the following properties from type ‘IAppConfig’: env, apiServer

    16 this.http.get(jsonFile).toPromise().then((response : IAppConfig) => {
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      1. try

        load() {
        const jsonFile = assets/config/config.json;

        return new Promise(async (resolve, reject) => {
        const source$ = this.http.get(jsonFile);
        const response = await lastValueFrom(source$);

        try {
        AppConfigService.settings = response as IAppConfig;
        console.log(‘Config Loaded’);
        console.log(AppConfigService.settings);
        resolve();
        } catch (error) {
        reject(Could not load the config file);
        }
        });
        }

  2. This approach leads to expose json config in browser window and in network tab developer console.Is there a way to avoid this kind of issues?

    1. No.
      Angular itself is a client-side app and runs in the browser. everything is downloaded to the client.

    2. Yes, you can use import to read the config file –
      e.g.
      document.addEventListener(‘DOMContentLoaded’, () => {
      import(
      /* webpackChunkName: “injectable-[request]” */
      ./config/${environment.config.file}
      ).then(configModule => {
      platformBrowserDynamic([
      {
      provide: APP_CONFIG,
      useValue: configModule.config
      }
      ])
      .bootstrapModule(AppModule)
      .catch(err => console.log(err));
      });
      });

  3. I am running Angular 6, and this solution does not work for me, and from my search I also saw a number of developers complaining about this.
    The problem I am running into is that the RESPONSE from the promise is never defined. (“this.http.get(jsonFile).toPromise().then((RESPONSE: IAppConfig)” )
    Is there anything I can do? Is there anything I am doing wrong? Or is this just nor working for Angular 6?

    1. Sorry for the late reply

      I Have tested with Angular 7 & 8. But I do not see any reason, why it should not run in Angular 6.

      If you could upload the sample code to git, then I can take a look.

      I Have also attached the Source code used in this example for your reference

Leave a Comment

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.

Scroll to Top