How to handle async providers in Angular

How to handle async providers in Angular

An elegant solution to a common problem.

ยท

2 min read

Loading a configuration at the start of an Angular app is a common task. Traditionally, you would utilize APP_INITIALIZER to accomplish this (Angular InDepth has a great piece on this). This works great most of the time.

There are, however, a few situations where this is suboptimal. For example when you are using an internal Component Library that has too much logic (๐Ÿ™„) and already has APP_INITIALIZERS hooks setup. This can cause issues where the execution order of all the hooks is not how you need it to be.

Well, how do we solve this?

There is actually a really easy solution to this - and I don't think a lot of people know about it.

You probably know that every Angular project has a file called main.ts that usually looks something like this:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.error(err));

Notice the call to platformBrowserDynamic? If you check the official docs for this you can see that this function actually takes an array of providers as a parameter. Perfect!

With that in mind, the solution looks like this:

  1. Load whatever you need in the main.ts file using the native fetch API
  2. Provide the value before the application is bootstrapped
  3. ???
  4. Profit

Now, your main.ts could look like this:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { BASE_API_URL_TOKEN } from './app/injectors';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

async function main() {
  try {
    const data = await fetch('/assets/config.json');
    const config = await data.json();

    await platformBrowserDynamic([
      { provide: BASE_API_URL_TOKEN, useValue: config.baseUrl },
    ]).bootstrapModule(AppModule);
  } catch (error) {
    console.error(error);
  }
}

main();

And that's it. You can checkout the example repo on GitHub

Conslusion

Always read the docs!

Cheers!

ย