How does APP_INITIALIZER work? So what do you need to know about dynamic configuration in Angular?
When we develop an application designed to run on multiple environments, we have to decide how to provide a configuration of appropriate variables depending on those environments
TLDR; 📕
environment.ts is not the best way to provide an environment configuration
do not introduce environmental configuration in build-time
Special injection token called APP_INITIALIZER is used for convenient and effective external configuration providing
I’ve prepared for you the minimal example which solves this problem by using APP_INITIALIZER. You can find it on StackBlitz
First round ❓
The simplest example of an environmental variable is API configuration. Commonly, we use a different URL depending on target environment. For instance:
In many Angular projects that I have seen, this problem has been solved by using analogous files provided by the Angular framework: environment.dev.ts, environment.test.ts and environment.prod.ts.
These files for our example would look like this
It cannot be said that this approach is very bad or does nor meet the assumptions made. However, look what happens if you choose this way to keep your variables and want to complete the full cycle of application delivery to production.
If everything works fine locally, the application is built on the development environment using the environment.dev.ts file
If everything works fine in the development environment, we can promote it to the test environment. In order for the application to run in the test environment, the application must be rebuilt using the environment.test.ts file this time
If the application has been tested in the test environment and is ready for production, we have to take the same steps. So we need to rebuild the application again using environment.prod.ts
Please note one fact. The application must be built three times (!!!) before it goes from the developer’s hand to the production environment.
The first conclusions that arise are: 👈
If you want to deliver a small change to production, you have to repeat the same building process several times, which will significantly extend the delivery time.
You can never be sure that a tested artifact from the dev environment or test will build correctly on the prod environment
Based on these conclusions and my own experience, I can safely and honestly tell you that environment.ts files are not a good place to hold most types of configuration. More precisely:
Certain configuration types should not be shipped when building an artifact. Storing environment-dependent variables in Angular’s environment.ts is the anti-pattern. Unfortunately, common anti-pattern.
So if you choose not to hold your configuration in environment.ts, what can you do?
Round 2 — APP_INITIALIZER approach 💪
Angular’s Injection Token APP_INITIALIZER comes with great help, thanks to which you can introduce additional initialization functions to your application. This functionality of the Angular framework ensures that the provided functions are executed when an application is started, while an application is still being initialized. These functions return a Promise object and an Angular’s app will not be initialized until it is being executed.
Is is a proper way to pass the configuration?
Of course! Even in the official Angular framework documentation, you can read: 📕
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. That way, the function is executed during the application bootstrap process, and the needed data is available on startup.
Follow me on Twitter if you want to be updated about new great Angular and frontend stuff! 😄
Runtime configuration in practice
Firstly, we need a file to hold configuration details in, and it will not be part of JavaScript artifacts which are built when ng build is executed. This configuration file should be downloaded on demand when the application is initialized.
for the following assumed configuration model.
We also need a service that has the responsibility to download the configuration and make it available while the application is running.
The last step is to use the above method with the APP_INITIALIZER token. It is used in the providers section for a specific NgModule.
It’s worth to consider creating a separate module dedicated to fetching configuration instead of adding the following code to the main app.module.ts
The loadConfiguration function is provided with the useFactory parameter. The function itself looks like this
By following the steps above, we ensure the following behavior for our application
when the application is started, the loadConfiguration from the ConfigurationLoader instance is run and it starts downloading the configuration from /assets/config/configuration.json
the application waits until the configuration is loaded from /assets/config/configuration.json
Finally, the application is initialized and the configuration is available through getConfiguration method of ConfigurationLoader instance
The magic is that the configuration placed in the assets directory is not taken into account at the time of building. You can modify the contents of the assets directory at any time without rebuilding the app. So the scheme for delivering the application to production now looks like this
If everything works fine locally, the application is built on the development environment and the /assets/config/configuration.json file is delivered externally
If everything works fine in the development environment, the application is promoted to the test environment. In order to run the application in the test environment, we take the artifact already built and replace the /assets/config/configuration.json file, which this time contains the configuration specific to the test environment
If the application is passed the tests in the test environment and is ready to go to the production, we take the same artifact again, push it to the prod environment and replace the analogous configuration file /assets/config/configuration.json for the prod environment.
Wow! By using APP_INITIALIZER and runtime configuration approach, you are able to limit the process of building the application in the entire delivery cycle to only one time! Frankly speaking, this one time is the maximum number of times according to the best practices. You also ensured that your built artifact on the initial environment is equal to a single byte with what is placed in the final environment.
If you want to see how this solution works, I have prepared a live example on ***StackBlitz*** for you.
Summary 📕
environment.ts is not the best way to provide an environment configuration in Angular ecosystem. We can safely name it as anti-pattern.
do not introduce an environment configuration which is runtime-dependant
Injection Token — APP_INITIALIZER is used for convenient and effective external configuration delivery in Angular ecosystem.
I have prepared for you a minimal example of solving the problem by using APP_INITIALIZER. You will find him on ***StackBlitz***
The feedback is important and best way in medium.com is clicking multiple times on clap icon 👏 on your left . If you click on it, I will know that my articiles has value for you ❤️ and I will be encouraged to write more 📙. Thanks!
Follow me on Twitter if you want to be updated about new great Angular and frontend stuff! 😄
Top comments (0)