An Introduction to Environment Variables and How to Use Them

Decoupling configuration from the application

Photo by Antoine Dautry on Unsplash

What Are Environment Variables?

Two fundamental components of any computer programming language are variables and constants. Like independent variables in a mathematical equation, these take on values that change the results of the program. Variables and constants both represent unique memory locations containing data the program uses in its calculations. The difference between the two is that variables values may change during execution, while constant values cannot be reassigned.

An environment variable is a variable whose value is set outside the program, typically through functionality built into the operating system or microservice. An environment variable is made up of a name/value pair, and any number may be created and available for reference at a point in time.

# Meteorite dataset from Nasa Open Data Portal
REACT_APP_METEORITE_LANDING_HOMEPAGE="https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh"
REACT_APP_METEORITE_STRIKE_DATASET="https://data.nasa.gov/resource/y77d-th95.json"

During application initialization, these are loaded into process.env and accessed by suffixing the name of the environment variable as shown below.

fetch(process.env.REACT_APP_METEORITE_STRIKE_DATASET)
.then((response) => {
return response.json();
})
.then((strikesJSON) => {
this.setState({ meteoriteStrikes: strikesJSON });
this.setState({ isDataLoaded: true});
});

At runtime, the reference to the environment variable name is replaced with its current value. In this case, process.env.REACT_APP_METEORITE_STRIKE_DATASET is replaced by its value, "https://data.nasa.gov/resource/y77d-th95.json".

The primary use case for environment variables is to limit the need to modify and re-release an application due to changes in configuration data. From the example above, whenREACT_APP_METEORITE_STRIKE_DATASET's URL changes there’s no need for source code alterations, testing, and deployment of the modified application.

Modifying and releasing application code is relatively complicated and increases the risk of introducing undesirable side effects into production. When the URL is defined by an environment variable instead the application, the change process consists of checking the validity of the new URL, updating the corresponding environment variable using an operating system command or updating a configuration file, and testing affected application function(s) to ensure the application still works as expected.

Use cases for environment variables include but are not limited to data such as:

  • Execution mode (e.g., production, development, staging, etc.)
  • Domain names
  • API URL/URI’s
  • Public and private authentication keys (only secure in server applications)
  • Group mail addresses, such as those for marketing, support, sales, etc.
  • Service account names

What these have in common are their data values change infrequently and the application logic treats them like constants, rather than mutable variables.

Next, let’s look at how to utilize environment variables using the native operating system, the NPM package dotenv, and webpack.

Environment Variables in NodeJS

Figure 1 — OS Environment Variables

Using environment variables in backend applications relies on operating system commands to define the environment variable and its value. A system administrator may define these using a command line interface, but it typically makes more sense to do so via a shell script. Environment variables typically aren’t globally accessible across the OS, they usually session-specific. For example, using the Linux command line:

setenv REACT_APP_METEORITE_LANDING_HOMEPAGE = "https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh"

At runtime, NodeJS automatically loads environment variables into process.env to make them available to the application. For example, fetch(process.env.REACT_APP_METEORITE_STRIKE_DATASET).

Management and manipulation of environment variables differ from operating system to operating system. Also, this varies across different microservices environments, like Heroku, where managing environment variables are performed using an administration panel. Due to this, understanding platform-specific factors is essential before using environment variables in your application.

One way to minimize these differences is to use the cross-env NPM package which provides an operating system independent POSIX-compatible command to set environment variables..

Environment Variables in the dotenv Package

Support for using environment variables in frontend applications isn’t an “out-of-the-box” feature of either the browser or Javascript; a package like dotenv is required to enable it. For the record, both frontend and backend applications may utilize dotenv.

Using this package is as easy as,

import dotenv from 'dotenv';
dotenv.config();
console.log(process.env.REACT_APP_METEORITE_STRIKE_DATASET);

This technique externalizes data by moving it from source code into environment variables in a .env file. Adding the .env file name to .gitignore prevents git push commands from uploading it to the GitHub repo where, for public repos, it would be available to anyone.

Figure 2 — .env File Usage

Environment variables in .env are formatted as name=value, lines starting with # are treated as comments, and blank lines are ignored. For example,

# Meteorite dataset from Nasa Open Data Portal
REACT_APP_METEORITE_LANDING_HOMEPAGE="https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh"
REACT_APP_METEORITE_STRIKE_DATASET="https://data.nasa.gov/resource/y77d-th95.json"

However, many popular packages such as Create React App (react-scripts), Gatsby, GraphQL CLI, Node Lambda, and more already include dotenv. If you already use one of these packages dotenv may already be available for use in your application. For example, the code snippets above are from an application generated by Create React App, which requires environment variables to be prefixed by REACT_APP_.

In the case of Create React App, there is no need to call dotenv.config() since node_modules/react-scripts/config/env.js automatically populates process.env with the contents of the .env file when the application starts. For an example of a Create React App refer to the Meteorite Explorer repo on GitHub.

Since the browser environment isn’t secure applications must take special care not to expose sensitive information, like application secrets. For additional information about how to protect frontend environments check out “Protect Application Assets: How to Secure Your Secrets”.

Environment Variables in webpack

webpack is a bundler that transforms, bundles or packages many different modules, resources, and assets in an application together for use in a browser. One common use of webpack is to prepare an application for production deployment. For example, Create React App’s build script uses webpack to create the build directory containing the production version of an application.

Although webpack implements support for using environment variables it’s as an option of the webpack command. For example,

webpack --env.NODE_ENV=local

Multiple environment variables are supported by specifying more than one --env option in the webpack command. These are referenced in webpack configuration files (e.g., webpack.config.js) as env. suffixed by the environment variable name. For example, console.log(env.NODE_ENV).

webpack configuration files may also reference environment variables defined by the operating system using process.env just like any other Javascript module. Consider this example from webpack.config.prod.js in Create React App.

// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';

Wrapping It Up

“Abstraction brings the world into more complex, variable relations; it can extract beauty, alternative topographies, ugliness, and intense actualities from seeming nothingness.” — Jerry Saltz

Using environment variables is one technique to make your app easier to configure by separating infrequently changing data from your code. But as simple as this technique may be, its use is influenced by considerations such as the application type (frontend or backend) and the operating environment (operating system or microservice).

Exploiting environment variables is easy, but understanding their nuances and being able to efficiently and securely utilize them is one factor that sets experienced Web Developers apart from inexperienced developers. As with any technology, the trick isn’t knowing how to use something, it’s knowing when to use it.