Coder.Haus | Thoughts on Angular Runtime Configuration with APP_INITIALIZER
We're a family of geeks who live to design scalable software and hardware solutions. We focus on solid, simple designs focused on industry good standard practices.
ITIL, SLA, Service Level Agreement, Change, Change Management, Development, Coding, Programming, Javascript, Java, Kotlin, Arduino, RaspberryPi, RPi, Android, VSCode, Hacker, Maker, Infrastructure
16081
post-template-default,single,single-post,postid-16081,single-format-standard,ajax_fade,page_not_loaded,,qode-title-hidden,qode_grid_1300,qode_popup_menu_push_text_top,qode-content-sidebar-responsive,qode-theme-ver-17.2,qode-theme-bridge,disabled_footer_bottom,wpb-js-composer js-comp-ver-5.6,vc_responsive
person running

Thoughts on Angular Runtime Configuration with APP_INITIALIZER

As often triggers posts I write, I was working on a project where I needed to store config files that were accessible without recompile. While going directly to a URI and grabbing data from a data base or data store is fine for running applications, in my experience initial configuration is better served locally. That is, you add the initial API URI, and a few other startup configurations in a file on the local server. In the event that your configuration server is down or slow, your app still starts up with basic config.

While there are a few npm packages to handle configuration, such as configstore and configuration, it’s simple enough to add configuration management to an Angular app. Here we have set up APP_INITIALIZER and a service to handle grabbing and making configurations available. In this post we talk about initial setup, issues with data availability, and how you might protect your configuration from web access.

Initial setup

Our first order of business is learning if anyone has done this before, and standing on the shoulders of giants. This, in fact, is a widely used pattern. Most often folks are using APP_INITIALIZER to set up pre-boot configuration. A great walkthrough of this setup is found on Microsoft’s DevBlogs https://devblogs.microsoft.com/premier-developer/angular-how-to-editable-config-files/. This has an easy to follow setup with this pattern, so we are not going to recreate their great post here.

Data availability at startup

One issue folks seem to have with this setup, is the availability of data on startup. Looking at this github issue, there are a few cases where configuration data is not available to other services that are initialized at app startup. While I haven’t seen a full codebase to really understand this problem from other’s perspective, looking through many github issues on this problem it seems that part of the problem is circular dependencies. For example, having a service that is a dependency of the config service, also depend on the config service for settings. Obviously this will not work!

In the screenshot below we see that at initialization our service, after our config service is called, named some.service, does not have any configuration data and the method to load our configuration data isn’t called until after our some.service is instantiated. What a mess! Right?

In the code below in some.other.service, we inject and call some.service in our someOtherMethod() method.

export class SomeOtherService {

  constructor(private someService: SomeService) {
    this.someOtherMethod();
  }

  someOtherMethod() {
    console.log('some-other.service someOtherMethod - the config says ', ConfigService.settings);
    console.log(
      'some-other.service someOtherMethod - calling someService.someMthod ',
      this.someService.someMethod()
    );
  }
}

And as the output below clearly shows, after the configuration load is completed, and the service is injected into another service, the configuration data is now in some.service for the asking!

While I know there are cases where this might not always be the case, from what I have read this is particularly true with deeply nested methods, in most cases our APP_INITIALIZER configuration will be available for use immediately on startup. At startup is where I mention above we need this, having the configuration available to load a URI to begin grabbing the rest of your configuration that might live on a data store. I also have not found any good examples showing these cases of a config not available, so we cannot really be sure if those solutions have underlying architectural issues.

Storing configs in the assets folder

If we navigate to http://localhost:4200/assets/config/config.dev.json, we find we have an unfortunate situation. Our configuration file is world readable!

While I don’t believe you should rely on security by obscurity, in our initial startup configuration we might need to include an API key for our configuration data store, and we might not want our configuration data store URI this easily out in the open. So what can we do? There are several ways you can handle this. You could set up an express server to serve the config file locally, using permissions to only allow the server to read the file. You could play with file permissions. You could set up the directory structure in such a way in assets where it isn’t easily known where the initial config file is. There are a few ways you can handle this, what works for you is dependent on your comfort level and the types of data you have in your config.

Wrapup

While I would always recommend using a pre-built and vetted solution rather than building your own, so you can focus on your domain case, setting up your Angular app to have its configuration grabbed at runtime is a fairly easy thing to do yourself. There could be downsides, depending on your architecture, such as issues with configuration data not being available quickly enough to satisfy some dependencies. There’s also the question of the configuration file being widely readable by the browser. Even with these potential issues, using configurations grabbed outside of code is the right thing to do. It makes maintenance simpler and quicker, as you will not have to recompile your solution and deploy to Production for a simple change, and makes your solution more extensible.

Tags: