angularconfigurationangular-standalone-components

Angular 17 external configuration


I am building a standalone Angular 17 app and I have come to the point where I want to have an external configuration file, where I could place the Rest API URL for example. I searched the web and found an approach with the APP_INITIALIZER token and tried it but it does not behave as expected. When I export the project using "ng build" and place it to a nginx server, the configuration is loaded successfully. The problem is that if I want to change the values inside the config.json, nothing happens. Even if I restart the nginx server, the values are still the ones from the first start. It is like the initialization works only the first time the app is started and then it is cached or stored somehow. All the existing sources I found online use a @NgModule decorator for older Angular versions, but in Angular 17 I do not think I need one? Are there other preferrable approaches for external configuration in Angular?

Here is some code:

main.ts

bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));

app.config.ts

function initConfigService(configService: ConfigService): () => Observable<any> {
  return () => configService.loadConfig();
}

export const provideConfigService = (): Provider => {
  const provider: (Provider) =
  {
    provide: APP_INITIALIZER,
    deps: [ConfigService],
    useFactory: initConfigService,
    multi: true
  };
  return provider;
};

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideClientHydration(), provideAnimationsAsync(), provideHttpClient(
    withInterceptors([authInterceptor]), withFetch()
  ), provideConfigService()]
};

configService.ts

@Injectable({
  providedIn: 'root'
})
export class ConfigService {
  _apiUrl: string = '';
  _enableFeatureX: boolean = false;

  constructor(private httpClient: HttpClient) {
  }

  loadConfig(): Observable<any> {
    return this.httpClient.get("../assets/config.json").pipe(
      tap((data: any) => {
        this._apiUrl = data.apiUrl;
        this._enableFeatureX = data.enableFeatureX;
      })
    );
  }

  getApiURL(): string {
    return this._apiUrl;
  }

  getEnableFeatureX(): boolean {
    return this._enableFeatureX;
  }
}

Solution

  • Just a wild guess, but you could just cache bust the asset file like so!

    Cache Busting

    Either the server / browser or service worker might cache the file and cause the issue!

      loadConfig(): Observable<any> {
        return this.httpClient.get(`../assets/config.json?v=${Math.random().toString().substring(2)}`).pipe(
          tap((data: any) => {
            this._apiUrl = data.apiUrl;
            this._enableFeatureX = data.enableFeatureX;
          })
        );
      }