Search code examples
angularapolloapollo-angular

How to load runtime configuration and use it in an angular module?


Following the Apollo Angular setup

src/app/graphql.module.ts

import { NgModule } from '@angular/core';
import { APOLLO_OPTIONS } from 'apollo-angular';
import { ApolloClientOptions, InMemoryCache, createHttpLink } from '@apollo/client/core';
import { HttpLink } from 'apollo-angular/http';

const uri = 'http://localhost:3000/graphql'; // <-- add the URL of the GraphQL server here
export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> {
  return {
    link: httpLink.create({ uri }),
    cache: new InMemoryCache(),
  };
}

@NgModule({
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink],
    },
  ],
})
export class GraphQLModule {}

This gives us a hardcoded URI variable that will obviously not work outside of local development. I would like to be able to change the URI by loading a configuration file at runtime, as opposed to build time (i.e. not using environment.ts). After reading Runtime environment configuration with Angular, this seems like a reasonable approach.

assets/config.json

Default configuration for local development that will be overridden on deployment

{
  "apiUrl": "http://localhost:3000/graphql"
}

src/app/app-config.service.ts

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

interface AppConfig {
  apiUri: string;
}

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

  constructor(http: HttpClient) {
    this.http = http;
  }

  async loadAppConfig() {
    this.appConfig = await this.http.get('/assets/config.json')
      .toPromise() as AppConfig;
  }

  public get apiUri() {
    return this.appConfig.apiUri;
  }
}

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { GraphQLModule } from './graphql.module';
import { AppConfigService } from './app-config.service';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, GraphQLModule, HttpClientModule],
  providers: [
    {
      provide : APP_INITIALIZER,
      multi : true,
      deps : [AppConfigService],
      useFactory : (appConfigService : AppConfigService) =>  () => appConfigService.loadAppConfig()
    }
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

So my question is, how do I use the AppConfigService in the GraphQLModule to get the apiUrl and set that to the uri in the createApollo factory function?


Solution

  • import {NgModule} from '@angular/core';
    import {APOLLO_OPTIONS} from 'apollo-angular';
    import {ApolloClientOptions, InMemoryCache} from '@apollo/client/core';
    import {HttpLink} from 'apollo-angular/http';
    import {ConfigService} from './config.service';
    
    export function createApollo(
      httpLink: HttpLink,
      configService: ConfigService
    ): ApolloClientOptions<any> {
      return {
        link: httpLink.create({uri: configService.apiUri}),
        cache: new InMemoryCache(),
      };
    }
    
    @NgModule({
      providers: [
        {
          provide: APOLLO_OPTIONS,
          useFactory: createApollo,
          deps: [HttpLink, ConfigService],
        },
      ],
    })
    export class GraphQLModule {}