Using environment variables in Angular applications

Angular executors (e.g. @nx/angular:webpack-browser and @angular-devkit/build-angular:browser) don't have built-in support for using environment variables when building applications.

To add support for environment variables we need to use the webpack DefinePlugin in our own custom webpack configuration. We'll see how to do so in the following sections.

A note on NODE_ENV

The webpack-based Angular executors (e.g. @nx/angular:webpack-browser and @angular-devkit/build-angular:browser) set the webpack's mode configuration option based on the values for the following in the builder options:

  • optimization
  • optimization.scripts
  • optimization.styles
  • optimization.styles.minify

If any of the above is set to true, webpack's mode is set to production. Otherwise, it's set to development.

By default, webpack automatically sets the NODE_ENV variable to the value of the mode configuration option. Therefore, Angular applications code have access to that environment variable at build time, but we can't change the NODE_ENV variable value directly as we would do with other environment variables because Angular always set the mode configuration option based on the above.

To change the NODE_ENV variable we can do one of the following:

  • Turn on the builder optimizations to set it to production
  • Turn off the builder optimizations to set it to development
  • Use a custom webpack configuration to override the webpack mode set by Angular executors

The first two options is a matter of changing your build target configuration or passing the specific flag in the command line. We'll see how to do the last in the following section.

Use a custom webpack configuration to support environment variables

First, install @types/node so we can use process.env in our code.

โฏ

npm add -D @types/node

Next, update the build and serve targets (in project.json or angular.json file), to the following.

1{ 2 "build": { 3 // NOTE: change the executor to one that supports custom webpack config. 4 "executor": "@nx/angular:webpack-browser", 5 // snip 6 "options": { 7 // NOTE: This file needs to be created. 8 "customWebpackConfig": { 9 "path": "apps/myapp/webpack.config.js" 10 } 11 // snip 12 } 13 }, 14 "serve": { 15 // NOTE: use dev-server that supports custom webpack config. 16 "executor": "@nx/angular:dev-server" 17 // snip 18 } 19} 20

Then, we can use DefinePlugin in our custom webpack.

apps/myapp/webpack.config.js
1const webpack = require('webpack'); 2 3function getClientEnvironment() { 4 // Grab NX_* environment variables and prepare them to be injected 5 // into the application via DefinePlugin in webpack configuration. 6 const NX_APP = /^NX_/i; 7 8 const raw = Object.keys(process.env) 9 .filter((key) => NX_APP.test(key)) 10 .reduce((env, key) => { 11 env[key] = process.env[key]; 12 return env; 13 }, {}); 14 15 // Stringify all values so we can feed into webpack DefinePlugin 16 return { 17 'process.env': Object.keys(raw).reduce((env, key) => { 18 env[key] = JSON.stringify(raw[key]); 19 return env; 20 }, {}), 21 }; 22} 23 24module.exports = (config, options, context) => { 25 // Overwrite the mode set by Angular if the NODE_ENV is set 26 config.mode = process.env.NODE_ENV || config.mode; 27 config.plugins.push(new webpack.DefinePlugin(getClientEnvironment())); 28 return config; 29}; 30

Now, when we define variables in our .env file, such as...

1# apps/myapp/.env 2NX_API_URL=http://localhost:3333 3
Set environment variables from the terminal

Alternatively, you can set the variable when running a terminal command by using:

  • MacOS & Linux: NX_API_URL=http://localhost:9999 npm run build-prod
  • Windows: set NX_API_URL=http://localhost:9999 & npm run build-prod

Finally, We can use environment variables in our code. For example,

apps/myapp/src/main.ts
1import { enableProdMode } from '@angular/core'; 2import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3import { AppModule } from './app/app.module'; 4 5if (process.env['NODE_ENV'] === 'production') { 6 enableProdMode(); 7} 8 9// This is defined in our .env file. 10console.log('>>> NX_API_URL', process.env['NX_API_URL']); 11 12platformBrowserDynamic() 13 .bootstrapModule(AppModule) 14 .catch((err) => console.error(err)); 15

You should also update tsconfig.apps.json and tsconfig.spec.json files to include node types.

1{ 2 "extends": "./tsconfig.json", 3 "compilerOptions": { 4 // snip 5 "types": ["node"] 6 } 7 // snip 8} 9

Using environment variables in index.html

While you cannot use variable in index.html, one workaround for this is to create different index.*.html files, such as index.prod.html, then swap it in different environments.

For example in project.json (or angular.json),

project.json or angular.json
1{ 2 "build": { 3 "executor": "@angular-devkit/build-angular:browser", 4 // snip 5 "configurations": { 6 "production": { 7 // snip 8 "fileReplacements": [ 9 { 10 "replace": "apps/myapp/src/environments/environment.ts", 11 "with": "apps/myapp/src/environments/environment.prod.ts" 12 }, 13 { 14 "replace": "apps/myapp/src/index.html", 15 "with": "apps/myapp/src/index.prod.html" 16 } 17 ] 18 } 19 } 20 } 21} 22
Optimize

You can also customize your webpack configuration, similar to using DefinePlugin above. This approach will require post-processing the index.html file, and is out of scope for this guide.