DEV Community

Cover image for Read environment variables from .env file in Angular
Mirza Leka
Mirza Leka

Posted on

Read environment variables from .env file in Angular

This article will teach you how to read environment variables from .env files into multiple Angular environments.

What is the .env file?

The .env file is a plain-text file used to store sensitive data and environment-specific configuration for a software application. Sample file:

GREETING=HELLO WORLD
SECRET=12345
Enter fullscreen mode Exit fullscreen mode

By default, Angular uses the Environment files built into the framework (under src/environments), which are good for runtime but are publicly visible (and thus expose environment secrets). To get around this, we'll create a .env file for each environment (dev, staging, production) and load those variables into the Angular env file.

How are we going to implement this?

  • Create an .env file for each environment
  • Create a Node.js script that loads those .env files into memory
  • Use the script to override Angular environment project files with values from the .env files
  • Run the Angular build

This solution adds environment variables to the project before build time. So the latest build has variables baked in.

This solution is suitable if:

  • You're doing local development ✅
  • You're hosting the application (dist files) yourself ✅
  • You're building a Docker image (for any environment) ✅

It is not helpful if you need to add environment variables after the build (through some UI on AWS, GitHub, or elsewhere). ❌

You can grab the full code on this repository.

Implementation

1️⃣ Creating environment files

Create a separate .env file for each environment in the root directory:

.env.development
.env.staging
.env.production
Enter fullscreen mode Exit fullscreen mode

Create a scripts directory inside your root project folder. Then, initialize the npm project within this folder.

> npm init -y
angular-multi-env\scripts\package.json:

{
  "name": "scripts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

This will generate a package.json and a package-lock.json file.

Install the dotenv package to load environment variables from the selected .env file. This will generate the node_modules directory.

> npm i dotenv
Enter fullscreen mode Exit fullscreen mode

Then create the index.js file in the scripts directory. This is where we'll write the script.

Finally, update the package.json file and add "type": "module".

2️⃣ Loading environment files within the script

The idea here is that when someone runs a build with a particular environment flag, we'll load the variables from the .env file based on that flag. So if you open a CLI and run:

> npm run build production
Enter fullscreen mode Exit fullscreen mode

The script will look for the .env.production file and read variables only from that file. Likewise for development and staging environments.
We'll use the built-in Node.js command process.argv to read the "environment flag".

Start off by importing the required dependencies:

import dotenv from 'dotenv'; // the package we've just installed
import path from 'node:path'; // native node.js module
import fs from 'node:fs'; // native node.js module
Enter fullscreen mode Exit fullscreen mode

Then read the (env) argument (flag) from CLI:

  const environment = process.argv[2];

  if (!environment) {
    throw new Error(`Missing environment flag: ${environment}!`);
  }
Enter fullscreen mode Exit fullscreen mode

We'll also do some validation checks to stop anyone from adding dummy flags:

  const allowedEnvs = ['development', 'staging', 'production'];

  if (!allowedEnvs.includes(environment.toLowerCase())) {
    throw new Error(`Invalid environment selected: ${environment}!`);
  }
Enter fullscreen mode Exit fullscreen mode

We need to set up a full environment name (and extension), which will be .env.[flag]:

  const envFile = `.env.${environment}`;
Enter fullscreen mode Exit fullscreen mode

Now we have to locate this file on the disc:

  const currentWorkingDirectory = process.cwd();
  const pathToEnvFile = path.resolve(currentWorkingDirectory, envFile);

  if (!pathToEnvFile) {
    throw new Error(`Env file not found: ${pathToEnvFile}!`);
  }
Enter fullscreen mode Exit fullscreen mode

The process.cwd() is another command built-into Node.js that returns current working directory. For me, it prints:

'D:\\Angular\\angular-multi-env'
Enter fullscreen mode Exit fullscreen mode

So that the path to the .env file (pathToEnvFile) becomes:

D:\Angular\angular-multi-env\.env.development or
D:\Angular\angular-multi-env\.env.staging or
D:\Angular\angular-multi-env\.env.production
Enter fullscreen mode Exit fullscreen mode

Next, we use the dotenv package to load the variables from the .env file by specifying the path:

  dotenv.config({
    path: pathToEnvFile
  });
Enter fullscreen mode Exit fullscreen mode

The environment variables should appear in the process.env object. We can log the variables to the console to confirm that:

  console.table({
    environment,
    isProduction: process.env.production,
    greeting: process.env.greeting,
    apiBaseURL: process.env.apiBaseURL
  });
Enter fullscreen mode Exit fullscreen mode

Quick Demo

angular-multi-env> node scripts/index.js development

┌──────────────┬─────────────────────────────┐
│ (index)      │ Values                      │
├──────────────┼─────────────────────────────┤
│ environment  │ 'development'               │
│ isProduction │ 'false'                     │
│ greeting     │ 'Hello DEV!'                │
│ apiBaseURL   │ 'http://localhost:3000/api' │
└──────────────┴─────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

3️⃣ Load the Angular Environment files

Now we do the same thing for Angular. Locate the Angular environment file from the project folder:

  const angularEnvFile = `src/environments/environment.${environment}.ts`
  const pathToAngularEnvFile = path.resolve(currentWorkingDirectory, angularEnvFile);

  if (!pathToAngularEnvFile) {
    throw new Error(`Env file not found: ${pathToAngularEnvFile}!`);
  }
Enter fullscreen mode Exit fullscreen mode

4️⃣ Override Angular environment files

Use the Node.js FileSystem module to read the contents of the selected environment as a string.

  const currentContent = fs.readFileSync(pathToAngularEnvFile, 'utf8');
Enter fullscreen mode Exit fullscreen mode

If the file is empty, generate content. Otherwise, update it:

  let contentToOverride = '';

  if (!currentContent) {

    // If empty => create new content
    const newContent = `
    export const environment = {
  production: ${process.env.production === 'true'},
  apiBaseURL: '${process.env.apiBaseURL}',
  greeting: '${process.env.greeting}'
};
`;

    contentToOverride = newContent.trim();

  } else {

    // Update the existing contents of the Angular environment file (using RegEx)
    const updatedContent = currentContent
      .replace(/production: .*/, `production: ${process.env.production === 'true'},`)
      .replace(/apiBaseURL: .*/, `apiBaseURL: '${process.env.apiBaseURL}',`)
      .replace(/greeting: .*/, `greeting: '${process.env.greeting}',`);

    contentToOverride = updatedContent;
  }

  // Override the file content
  fs.writeFileSync(pathToAngularEnvFile, contentToOverride);
Enter fullscreen mode Exit fullscreen mode

5️⃣ Add build scripts for each environment

Update the package.json of the Angular project. Include scripts to run the environment script (for each environment.ts file you have).

I have three files:

/src/environments/environment.development.ts
/src/environments/environment.staging.ts
/src/environments/environment.production.ts
Enter fullscreen mode Exit fullscreen mode

So I'll add three scripts as prebuild scripts:

  "scripts": {
    "ng": "ng",
    "start": "ng serve",

    "prebuild:development": "node scripts/index development",
    "prebuild:staging": "node scripts/index staging",
    "prebuild:production": "node scripts/index production",

    "build:development": "ng build --configuration development",
    "build:staging": "ng build --configuration staging",
    "build:production": "ng build --configuration production",
  }
Enter fullscreen mode Exit fullscreen mode

The way prebuild scripts work is, if you run a build script, e.g.

> npm run build:development
Enter fullscreen mode Exit fullscreen mode

The NPM will run the prebuild:development first. So it will generate/update the environment.development.ts file and then run the selected script.

6️⃣ Final Demo

> npm run build:production

> angular-multi-env@0.0.0 prebuild:production
> node scripts/index production # Loading prebuild script

┌──────────────┬─────────────────────────┐
│ (index)      │ Values                  │
├──────────────┼─────────────────────────┤
│ environment  │ 'production'            │
│ isProduction │ 'true'                  │
│ greeting     │ 'Hello PROD!'           │
│ apiBaseURL   │ 'https://myapp.com/api' │
└──────────────┴─────────────────────────┘
Environment file updated!

> angular-multi-env@0.0.0 build:production
> ng build --configuration production

Initial chunk files   | Names         |  Raw size | Estimated transfer size
main-PQGIXNIB.js      | main          | 224.85 kB |                60.28 kB
polyfills-FFHMD2TL.js | polyfills     |  34.52 kB |                11.28 kB
styles-BT3MSNLR.css   | styles        |  54 bytes |                54 bytes

                      | Initial total | 259.42 kB |                71.62 kB

Application bundle generation complete. [1.991 seconds]
Enter fullscreen mode Exit fullscreen mode

Update .gitignore file

Don't forget to add .env files and the scripts/node_modules directory to the .gitignore file.

# Environments
.env
.env.*

# Node_modules
/scripts/node_modules

# Also exclude changes from the Angular environment files
/src/environments/*
Enter fullscreen mode Exit fullscreen mode

Once again, you can grab the full code here. If you're interested in learning how to Dockerize an Angular application, you can learn that here.

Bye for now 👋

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.