<p>I have an Angular 7 application where the <code>app.module.ts</code> files looks like the following. Note that in this example, there are 2 modules with each having 1 key whose value needs to be externalized. By <code>externalized</code>, I mean the values should be acquired from the environment at runtime.</p>

<pre><code>@NgModule({
 declarations: [ ... ],
 imports: [
  SomeModule.forRoot({ apiKey1: "needs to be externalized" }),
  AnotherModule.forRoot({ apiKey2: "needs to also be externalized" })
 ],
 providers: [ ... ],
 bootstrap: [AppComponent]
})
export class AppModule { }
</code></pre>

<p>What I do is build this application (e.g. <code>ng build</code> and then containerize it using Docker). At deployment time, the <code>DevOps</code> person wants to run the docker container as follows.</p>

<pre><code>docker run -e API_KEY_1='somekey' -e API_KEY_2='anotherkey' -p 80:80 my-container:production
</code></pre>

<p>Note that <code>API_KEY_1</code> should map to <code>apiKey1</code> and <code>API_KEY_2</code> should map to <code>apiKey2</code>. </p>

<p>Is there any disciplined way of externalizing values for an Angular application?</p>

<p>I thought about writing a helper script to do string substitution against the file, but I think this approach is not very disciplined (as the transpiled Angular app files are obfuscated and minified). The script would run at container startup, read the environment variables (key and value), and then look at the files to replace the old values with the ones from the environment.</p>

<p>Eventually, the Angular app will be orchestrated with Kubernetes. I'm wondering if there's anything there that might help or influence how to externalize the values in a best practice way.</p>

<p>Any help is appreciated.</p>

Jane Wayne
<p>You could use a substitution in custom entry-point.</p>

<pre class="lang-sh prettyprint-override"><code>FROM nginx

RUN apt-get update &amp;&amp; apt-get -y install gettext-base nginx-extras

ADD docker-entrypoint.sh /
ADD settings.json.template /

COPY dist /usr/share/nginx/html

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
</code></pre>

<p>With a <code>docker-entrypoint.sh</code> like this:</p>

<pre class="lang-sh prettyprint-override"><code>#!/bin/bash

envsubst &lt; "settings.json.template" &gt; "settings.json"
cp settings.json /usr/share/nginx/html/assets/

# Launch nginx
exec "$@"

</code></pre>

<p>And a <code>settings.json.template</code>:</p>

<pre><code>{
  "apiKey2": "$API_KEY_2",
  "apiKey1": "$API_KEY_1"
}
</code></pre>

<p>Then on your source you add a file
<code>settings-loader.ts</code></p>

<pre><code>export const settingsLoader = new Promise&lt;any&gt;((resolve, reject) =&gt; {
  const xmlhttp = new XMLHttpRequest();
  const method = 'GET';
  const url = './assets/settings.json';

  xmlhttp.open(method, url, true);

  xmlhttp.onload = function() {
    if (xmlhttp.status === 200) {
      const _environment = JSON.parse(xmlhttp.responseText);
      resolve(_environment);
    } else {
      resolve();
    }
  };

  xmlhttp.send();
});

</code></pre>

<p>And on your <code>main.ts</code>:</p>

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

import {AppModule} from './app/app.module';
import {environment} from './environments/environment';
import {settingsLoader} from 'settings-loader';

settingsLoader.then((settings) =&gt; {
  if (settings != null) {
    environment.settings = Object.assign(environment.settings, settings);
  }

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

  platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err =&gt; console.log(err));
});
</code></pre>

<p>Then you should have access in your code with </p>

<pre><code>import {environment} from '../environments/environment';

console.log(environment.settings.apiKey1);
</code></pre>
