Treinetic

Angular - Dynamic Configuration in Angular using APP_INITIALIZER

Angular using APP_INITIALIZER

Two primary methods exist in Angular apps for managing configuration settings: the APP_INITIALIZER token and environment.ts files. Both appear to hold the values needed to execute the program, at least at first look. Actually, they’re really different from one another.

Making use of environment.ts files

Because they are hard-coded into the final bundle, configuration parameters are integrated into your application’s construction process. While this method guarantees a speedy application startup, it does have some limits. Any changes to the configuration need rebuilding and redeploying the application, and bundle size might grow due to large settings.

Using the APP_INITIALIZER Token

This approach loads the configuration parameters at runtime. Updates may be applied without rebuilds or redeploys since the program retrieves the configuration as it begins. Applications that need varying settings for different surroundings or clients might greatly benefit from this technique.

The configuration element of the build becomes environment.ts, which in turn makes the bundle bigger and more difficult to change. But APP_INITIALIZER makes it possible to dynamically update the settings, which makes it more manageable and versatile. In this post, we’ll look at the alternative strategy.

Use case

Imagine a project that has to be deployed to several clients, each with their own unique configuration. It is recommended that the program loads the settings during runtime in order to keep the codebase consistent. It is important that the design permits changing the settings without modifying the application code. That is to say, the application shouldn’t need rebuilding or redeployment in order to make configuration changes. The following components will make up the setup in the provided example:

  • Brand name of the business
  • API URL—every customer uses different API hosts.
  • Theme—the default UI theme
  • Language
  • Activated functions including messaging, invoicing, and alerts
  • Email assistance

In order to meet the specified criteria, we will investigate the APP_INITIALIZER token and compare it to the conventional method outlined in the environment.ts file. The post goes on to show how to use Zod.js to check the configuration and make sure it fulfills all the requirements of the application.

APP_INITIALIZER Token

What exactly is APP_INITIALIZER? Let’s find out. It is what the official documents state:

A DI token that you can use to provide one or more initialization functions.

The provided functions are injected at application startup and executed during app initialization. If any of these functions returns a Promise or an Observable, initialization does not complete until the Promise is resolved or the Observable is completed.

You can, for example, create a factory function that loads language data or an external configuration and provide that function to the APP_INITIALIZER token. The function is executed during the application bootstrap process, and the needed data is available on startup.”

An Angular special token called APP_INITIALIZER may execute a function or functions upon application startup. When the app is first launched, these functions are invoked. To load configuration from a JSON file, for instance, you may write a function. This method will be executed automatically when the app begins by using APP_INITIALIZER. This ensures that the configuration is ready to use from the very beginning.

The next stage, after understanding the function of APP_INITIALIZER, is to show how dynamic configuration loading is implemented.

Using APP_INITIALIZER to Implement Dynamic Configuration

Essentially, the following procedures make up an example solution:

  • The program will get the config.json file upon launch.
  • A dedicated service will receive the parsed configuration material.
  •  We may then obtain it via the service and handle it as required.

The main.ts file is where the program begins, so let’s examine it:

function initializeAppFactory(

  httpClient: HttpClient,

  configService: ConfigService

) {

  const url = ‘./config.json’;

  return () =>

    httpClient.get(url).pipe(

      tap((config) => {

        const dto = parseDTO(config);

        if (dto.success) {

          configService.setConfig(dto.data);

        } else {

          console.error(‘Invalid config.json’, dto.error);

        }

      })

    );

}

bootstrapApplication(AppComponent, {

  providers: [

    provideHttpClient(),

    {

      provide: APP_INITIALIZER,

      useFactory: initializeAppFactory,

      multi: true,

      deps: [HttpClient, ConfigService],

    },

  ],

});

At startup, the application loads a configuration file called config.json using a factory method called initializeAppFactory. This verifies that the app has been properly configured prior to its launch.

Angular calls the initializeAppFactory method when it is starting up. Aside from HttpClient, it also injects ConfigService. To get the config.json file, an HTTP GET call is made using the HttpClient service. After retrieving the file, we use the zod.js library to parse its content. Before the program starts running, this method verifies the setup to be accurate and dependable. See the zod.js page for further information on how to parse and map API replies.

import { z } from ‘zod’;

// Define the schema for the config

const schema = z.object({

  companyName: z.string(),

  apiUrl: z.string(),

  theme: z.string(),

  language: z.string(),

  features: z.object({

    enableChat: z.boolean(),

    enablePayments: z.boolean(),

    enableNotifications: z.boolean(),

  }),

  supportEmail: z.string(),

});

// Infer the type from the schema

export type ConfigDTO = z.infer<typeof schema>;

// Parse the config if matches the schema

export function parseDTO(source: unknown) {

  return schema.safeParse(source);

}

We return to initializeAppFactory and give the settings to ConfigService if the DTO parsing was successful. As such, the service’s function is simple: it saves the configuration and makes it accessible to the application at large. Its primary function is to encapsulate the APP_INITIALIZER token. It is possible to add methods to the service that fetch specific elements of the configuration as the application and configuration expand. 

@Injectable({

  providedIn: ‘root’,

})

export class ConfigService {

  #config!: ConfigDTO;

setConfig(config: ConfigDTO) {

    this.#config = config;

  }

  getConfig() {

    return this.#config;

  }

}

Lastly, the configuration may be used by injecting the ConfigService into any portion of the app:

config = inject(ConfigService).getConfig();

Consider the configuration file (.json).

Take a look at this config.json file sample. It’s just a simple JSON file that may have all the data the app needs to function. For zod.js to work properly, the data structure must be compatible with the schema.

{

  “companyName”: “Customer One”,

  “apiUrl”: “https://api.customer1.com/v1/”,

  “theme”: “dark”,

  “language”: “en-US”,

  “features”: {

    “enableChat”: true,

    “enablePayments”: false,

    “enableNotifications”: true

  },

  “supportEmail”: “[email protected]

}

Neither during nor after development should the file be part of the bundle, which is a major plus. This allows for simple updates even after the app has been developed, for instance, on the server that hosts the application. You may make changes to this file, and they will be reflected instantly without having to reload the app. The JavaScript files can be left alone. You may also use it to deploy the same package with other settings. The setting-up process’s output is seen below. 

Click Here for More Interesting Posts. 

Comparison with environment.ts Files

Another method for controlling application settings via the environment. The ts file is offered by Angular, as stated in the introduction. Is it possible, however, for this method to do what the APP_INITIALIZER token can? In short, the answer is no. The contents of the .env file are hard-coded into the final application bundle since it is packed during the construction process. The inability to modify or alter the settings after application build and deployment is caused by this constraint.

To the contrary, you may adjust application behavior without rebuilding or redeploying the application by using the APP_INITIALIZER token to dynamically collect configuration data during runtime. For apps that need to be customized for various deployment settings, this is where APP_INITIALIZER really shines.

Problems with the Angular APP INITIALIZER TOKEN

There are a few drawbacks to using APP_INITIALIZER_TOKEN, notwithstanding its usefulness. A big drawback is that it might prevent your app from launching if you use it to get data from an external service. This happens in the event that the service has delays or issues. Consider the possibility that your app won’t launch at all due to a sluggish or otherwise troublesome service.

Nonetheless, a JSON file located on the same server as your app is used in the example provided in this article. Typically, delays do not pose an issue in this context. There are hardly any network delays since the file is locally stored. So, it’s safe to assume that the JSON file loading delay won’t cause too much trouble.

Wrapping up

An efficient method for configuring Angular apps dynamically during runtime is the APP_INITIALIZER token. You may adjust settings like API URLs, themes, default language, and feature choices quickly by loading a configuration file like config.json when the app begins. This eliminates the need to update the application code. When compared to the conventional setting, this method offers more leeway with the .ts file because it enables configuration updates even after app deployment and build. This facilitates client-specific app customization without requiring app rebuilding or redeployment.

Verifying the configuration data using Zod.js further guarantees its accuracy and reliability. Maintaining a single codebase across several client installations is made simpler with this strategy, and handling many configurations is simplified overall.

Spread the love