Using Environment Variables in Angular Applications
For Angular applications using ESBuild
Section titled “For Angular applications using ESBuild”Setting thedefine
option of theapplication
executor
Section titled “Setting the define option of the application executor”Support for thedefine
option requires Angular17.2.0 or later.
In Angular applications using the@nx/angular:application
or@angular-devkit/build-angular:application
executors, you can set thedefine
option in thebuild
target to define variables that will be available in your application code at build time:
{..."targets": {"build": {"executor":"@nx/angular:application","options": {..."define": {"MY_API_URL":"http://localhost:3333"}}},...}}
When you use one of the@nx/angular
executors for building your applications, make sure to also change theserve
executor to@nx/angular:dev-server
to ensure the extra features provided by Nx are also available when serving the application.
Next, make sure to inform TypeScript of the defined variables to prevent type-checking errors during the build. We can achieve this in a number of ways. For example you could create or update a type definition file included in the TypeScript build process (e.g.src/types.d.ts
) withdeclare
statements for the defined variables:
declare constMY_API_URL:string;
The above would allow you to use theMY_API_URL
variable in your application code as in the following example:
import { Injectable }from'@angular/core';@Injectable({ providedIn:'root' })exportclassApiHttpClient {constructor() {console.log('API URL:',MY_API_URL);}}
You can also define the variables in a way that allows you to consume them as you would do in Node.js applications:
{..."targets": {"build": {"executor":"@nx/angular:application","options": {..."define": {"process.env.MY_API_URL":"http://localhost:3333"}}},...}}
Like the previous example, you must configure TypeScript to recognize theprocess.env
object. You can do this by defining theprocess.env
object in a type definition file:
declare constprocess: {env: {API_URL:string;};};
You could also add the Node.js types to yourtsconfig.json
file, but this would add many types that you don't need in a browser environment. This can be misleading since you'll see some types are available, but the runtime functionality won't be. It's better to define only the types you need in a type definition file.
And then use the variable in your application code:
import { Injectable }from'@angular/core';@Injectable({ providedIn:'root' })exportclassApiHttpClient {constructor() {console.log('API URL:', process.env.MY_API_URL);}}
Using a custom ESBuild plugin
Section titled “Using a custom ESBuild plugin”Support for custom ESBuild plugins requires Angular17.0.0 or later.
The previous method is useful to statically define variables in the project configuration that will be available at build time. However, if you need to dynamically collect and define the environment variables available at build time, you can create a custom ESBuild plugin.
You can provide a custom ESBuild plugin to the@nx/angular:application
or@nx/angular:browser-esbuild
executors:
{..."targets": {"build": {"executor":"@nx/angular:application","options": {..."plugins": ["apps/my-app/plugins/env-var-plugin.js"]}},...}}
Next, create the custom ESBuild plugin:
constmyOrgEnvRegex =/^MY_ORG_/i;constenvVarPlugin = {name:'env-var-plugin',setup(build) {constoptions =build.initialOptions;constenvVars = {};for(constkeyinprocess.env) {if(myOrgEnvRegex.test(key)) {envVars[key] =process.env[key];}}options.define['process.env'] =JSON.stringify(envVars);},};module.exports=envVarPlugin;
The plugin collects all environment variables that start withMY_ORG_
and defines them in theprocess.env
object. You can adjust the plugin to your needs (e.g., use a different regular expression, use a whitelist, add all environment variables, etc.).
As shown in the previous section, add the defined variables to a type definition file to ensure TypeScript recognizes them.
Now, you can define variables in an.env
file, such as:
MY_ORG_API_URL=http://localhost:3333
Alternatively, you can alsoset environment variables when running a terminal command.
Finally, you can use the environment variables in your application code:
import { Injectable }from'@angular/core';@Injectable({ providedIn:'root' })exportclassApiHttpClient {constructor() {console.log('API URL:', process.env.MY_ORG_API_URL);}}
For Angular applications using Webpack
Section titled “For Angular applications using Webpack”Angular executors for Webpack (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 webpackDefinePlugin
in our own custom webpack configuration. We'll see how to do so in the following sections.
A note onNODE_ENV
Section titled “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'smode
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 totrue
, webpack'smode
is set toproduction
. Otherwise, it's set todevelopment
.
By default, webpack automatically sets theNODE_ENV
variable to the value of themode
configuration option. Therefore, Angular applications code have access to that environment variable at build time, but we can't change theNODE_ENV
variable value directly as we would do with other environment variables because Angular always set themode
configuration option based on the above.
To change theNODE_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
Section titled “Use a custom webpack configuration to support environment variables”Update thebuild
andserve
targets to use the@nx/angular
relevant executors and provide a custom Webpack configuration:
{..."targets": {"build": {"executor":"@nx/angular:webpack-browser","options": {..."customWebpackConfig": {"path":"apps/my-app/webpack.config.js"}}},"serve": {"executor":"@nx/angular:dev-server"...},...}}
Then, we can useDefinePlugin
in our custom Webpack configuration:
constwebpack =require('webpack');constmyOrgEnvRegex =/^MY_ORG_/i;functiongetClientEnvironment() {constenvVars = {};for (constkeyinprocess.env) {if (myOrgEnvRegex.test(key)) {envVars[key]=process.env[key];}}return {'process.env':JSON.stringify(envVars),};}module.exports=(config,options,context)=> {// Overwrite the mode set by Angular if the NODE_ENV is setconfig.mode=process.env.NODE_ENV||config.mode;config.plugins.push(newwebpack.DefinePlugin(getClientEnvironment()));returnconfig;};
In our custom Webpack configuration we collect all environment variables that start withMY_ORG_
, define theprocess.env
object with them, and provide it to theDefinePlugin
. You can adjust the configuration to your needs (e.g., use a different regular expression, use a whitelist, add all environment variables, etc.).
Next, make sure to inform TypeScript of the defined variables to prevent type-checking errors during the build. We can achieve this in a number of ways. For example you could create or update a type definition file included in the TypeScript build process (e.g.src/types.d.ts
) withdeclare
statements for the defined variables:
declare constprocess: {env: {MY_ORG_API_URL:string;};};
Now, we can define variables in our.env
file, such as:
MY_ORG_API_URL=http://localhost:3333
Alternatively, you can alsoset environment variables when running a terminal command.
Finally, we can use environment variables in our code:
import { Injectable }from'@angular/core';@Injectable({ providedIn:'root' })exportclassApiHttpClient {constructor() {console.log('API URL:', process.env.MY_ORG_API_URL);}}
Using environment variables inindex.html
Section titled “Using environment variables in index.html”While you cannot use environment variables inindex.html
, one workaround is to create differentindex.*.html
files, such asindex.prod.html
, then swap them in different environments.
For example, you can configure yourbuild
target inproject.json
as follows:
{..."targets": {"build": {"executor":"@angular-devkit/build-angular:browser",..."configurations": {"production": {..."fileReplacements": [{"replace":"apps/my-app/src/index.html","with":"apps/my-app/src/index.prod.html"}]}}}}}
You can also customize your webpack configuration, similar to usingDefinePlugin
above. This approach will require post-processing theindex.html
file, and is out of scope for this guide.