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 :
{
"API_URL": "http://localhost:3000"
}
Then we can make the service that will fetch the configs :
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
const initializerConfigFn = (appConfig: AppConfigService) => {
return () => {
return appConfig.loadAppConfig();
};
};
And provide it in the main module :
@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 :
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 :
@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
!
Top comments (0)