Runtime config with Angular
Tags: angular, javascript, devops
Published: 2019-06-17 18:08:47

---
title: Runtime config with Angular
published: true
description: Load a configuration at runtime with Angular
tags: angular,javascript,devops
canonical_url: https://nbe.io/load-a-configuration-at-runtime-with-angular/
cover_image: https://images.unsplash.com/photo-1557201874-98d4f7c33eb3?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ
series: Dynamic config with Angular and Docker
---

In angular, there is by default a buildtime config, it works well, but it have some disadvantages :
- To change a config, you have to rebuild your application
- Doesn't respect the "Build once, deploy everywhere" devops philosophy

For example, if you want to build your angular project into a docker image and use only the buildtime config, you will need to build every time the docker in order to point to another back-end !

## The solution is Runtime config.

Runtime config is a config file, usually fetched at startup, containing configs like the server URL or other details.

Luckily, Angular have a hook to run something at startup, `APP_INITIALIZER` and we can use this to fetch the config at startup !

First, let's make the `config.json` file, in the `assets` folder :
```json
{
  "API_URL": "http://localhost:3000"
}
```

Then we can make the service that will fetch the configs :


```ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class AppConfigService {
  private appConfig;

  constructor(private http: HttpClient) {}

  loadAppConfig() {
    return this.http
      .get('/assets/config.json')
      .toPromise()
      .then(data => {
        this.appConfig = data;
      });
  }

  getServerUrl(): string {
    return this.appConfig.API_URL;
  }
}
```

As you can see, you have to return a promise and not an observable, as `APP_INITIALIZER` needs a promise and not an observable.

Once we have this service, we have to edit the `app.module.ts` with a function to load the config

```ts
const initializerConfigFn = (appConfig: AppConfigService) => {
  return () => {
    return appConfig.loadAppConfig();
  };
};
```

And provide it in the main module :


```ts
@NgModule({
  imports: [
    ...
    HttpClientModule,
    ...
  ],
  providers: [
    ...
    {
      provide: APP_INITIALIZER,
      useFactory: initializerConfigFn,
      multi: true,
      deps: [AppConfigService],
    },
    ...
  ],
})
export class AppModule {}
```

Here we have it ! Your application will be run after the fetch of the config.json. 



## Bonus : auto http prefixer

With that, we can make a auto http prefixer using our previously fetch settings :


```ts
import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { AppConfigService } from '../services/app-config.service';

 /**
 * Prefixes all requests not starting with `http[s]` with the dynamic config.
 */
@Injectable()
export class ApiPrefixInterceptor implements HttpInterceptor {
  constructor(private readonly appConfig: AppConfigService) {}

   intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!/^(http|https):/i.test(request.url) && !request.url.startsWith('/assets/')) {
      request = request.clone({ url: this.appConfig.getServerUrl() + request.url });
    }
    return next.handle(request);
  }
}
```

Then provide it in our AppModule :


```ts
@NgModule({
  imports: [
    ...
    HttpClientModule,
    ...
  ],
  providers: [
    ...
    {
      provide: APP_INITIALIZER,
      useFactory: initializerConfigFn,
      multi: true,
      deps: [AppConfigService],
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: ApiPrefixInterceptor,
      multi: true
    },
    ...
  ],
})
export class AppModule {}
```

There you have it ! Automatic url prefixer from a dynamic url fetch from a `config.json` !