Loading App Configuration in the APP_INITIALIZER
Tags: angular, configuration
Published: 2019-05-29 22:47:51

---
title: Loading App Configuration in the APP_INITIALIZER
published: true
tags: [angular, configuration]
canonical_url: https://www.prestonlamb.com/blog/loading-app-config-in-app-initializer
---

If you're anything like me, you've been placing the configuration for your Angular app in the `environment.ts` file, and then copying that same file to `environment.test.ts` and `environment.prod.ts` files. Which isn't too bad; most of those values don't change too often. But sometimes they do. And when they do, that's when the pain starts.

So for me, this is what happens:

1. The app runs perfectly in my local development environment.
2. I build the application and deploy to the testing environment.
3. The application starts, but doesn't work as expected.
4. I change an environment value, rebuild the application, and redeploy.
5. The application starts, but doesn't work as expected.
6. I change an environment value, rebuild the application, and redeploy.
7. The application starts, but doesn't work as expected.
8. I change an environment value, rebuild the application, and redeploy.
9. ...

You get the picture. It's minor change after minor change, followed by deploys, that don't seem to fix the problem. And every build/deploy process takes 5 or 10 minutes. This translates to a lot of wasted time. And the thing is: most of those configuration values don't need to be present at build time. They can be set at runtime of the app. It's for that reason that the `environment.ts` files are _not_ the place for these configuration values.

For a long time I put up with this because it was all I knew. But a few weeks ago I was listening to [the Adventures in Angular podcast](https://devchat.tv/adv-in-angular/aia-203-where-to-store-angular-configurations-with-dave-bush/) and they were talking about the proper location to store your app's configuration. And I was stoked! That's what I needed! They were talking with a guy named Dave Bush, who had written this [blog post](https://davembush.github.io/where-to-store-angular-configurations/) on the subject. I felt like I was finally understanding what I needed to do to have an effective way to introduce run-time configuration values to my application.

I got started implementing this in my app, but I didn't get too far before I was a little stumped. I couldn't find any full code examples of doing what I wanted to do. In the end, after reading a couple other articles on the subject (including [this one](https://robinraju.github.io/developer/2017-11-14-loading-external-config-in-angular/) and [this one](https://juristr.com/blog/2018/01/ng-app-runtime-config/), on top of the already mentioned one above), I got a service working that loads the configuration file and provides it to places throughout the app. I've put together [this StackBlitz example](https://stackblitz.com/edit/app-config-in-initializer) to show the basics of the code you'll need. Below is a brief outline as well for those steps.

## Code Review

There are a few things to add to the application to load the configuration. The first is `app.module.ts`. Add the following function above the `@NgModule` decorator:

```ts
export function initConfig(appConfigService: AppConfigService) {
    return () => appConfigService.loadConfig();
}
```

This function simply takes the `AppConfigService` (which we'll look at in a minute) and returns a function calling the `loadConfig` function from the service. There's one really important part here though that had was causing issues for me for a few days: _ **DO NOT FORGET TO `export` THE FUNCTION!** _ I wasn't exporting that function, and I'm not sure why, but it messed up how the application builds. It doesn't produce an error, per se, but it wasn't building any lazy loaded modules. As soon as I added the `export`, everything built and ran as expected. So don't waste time like I did. 😃

The second addition to the application needed is a service, which I called `AppConfigService`. In part, that service looks like this:

```ts
constructor(private _http: HttpClient) {}

public loadConfig() {
    return this._http.get('./assets/config/config.json')
        .toPromise()
        .then((config: any) => {
            this.config = config;
            this.configSubject$.next(this.config);
        })
        .catch((err: any) => {
            console.error(err);
        })
}

public getConfig() {
    return this.config;
}
```

The `HttpClient` is necessary to be able to load the file. Also, the `APP_INITIALIZER` token requires a function that is a promise to be returned, so using the `toPromise` method on an Observable works perfectly. Another thing to point out is the `configSubject$` variable that is being used. The reason I implemented this is that some of the services were loading before the file was done loading. So having them subscribe to that subject and waiting for the value ended up working perfectly for me.

Another thing that seemed to cause issues for me was not explicitly marking functions as public. I'm not sure if this is a result of using the service in the `APP_INITIALIZER` factory, or what, but as soon as I explicitly marked variables and functions as public, things seemed to work as expected.

Alright, now let's jump back to the `app.module.ts` file once more. In the `providers` array of the `@NgModule` decorator, add the following:

```ts
{
    provide: APP_INITIALIZER,
    useFactory: initConfig,
    deps: [AppConfigService],
    multi: true
}
```

This tells the application to use the `APP_INITIALIZER` token, gives it a factory function to use, provides the needed dependencies, and lastly allows for using multiple `APP_INITIALIZER`s in your application.

After those few things are set up, you can then start using the configuration object in the service throughout your app. In the constructors for services, you can either use the `getConfig` method to get the configuration object or you can use the subject and subscribe to it, waiting for the configuration to be loaded. I mentioned this above, but some services were being loaded before the `APP_INITIALIZER` is done running. But it didn't happen every time. So, because of that, I checked multiple times in each service/module that needed to use this configuration object and determined if I needed the subject or not.

Now, there's one last step that needs to be taken to make this configuration truly run-time, and that's providing a different copy of the `config.json` file in each environment. In my case, we build our application and make a Docker image to run the application ([see this article for more info](/blog/dockerizing-angular)). So to get the file in there without having to re build the application, I added a volume in the `docker-compose.yml` file like this: `./config.json:/usr/share/nginx/html/assets/config/config.json`. This allowed me to create a `config.json` file on the server that was then copied into the application with new config values. You would be able to do the same thing if you cloned the application to your server and built and ran the application without putting it in a Docker container as well.

Hopefully this answers some questions for some of you. It took a few days and a lot of reading and testing to get all these pieces together, but I'm really happy with the end product. This will make changing many configuration options for our applications a lot easier, and faster too. We no longer have to go through the process of making the change, creating a PR, building new images, and deploying. It's now a simple change in a JSON file and restarting the Docker container. If this helps you out, I'd love to hear from you! Reach out and let me know!

## Update

To easily achieve the functionality discussed here, use this package: [runtime-config-loader](https://www.npmjs.com/package/runtime-config-loader). All it requires is adding the provider to your `app.module.ts` file, much like is described above.