DEV Community

Jim Medlock for Chingu

Posted on • Originally published at Medium on

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 are unchanging.

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"
Enter fullscreen mode Exit fullscreen mode

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});
});
Enter fullscreen mode Exit fullscreen mode

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, when REACT\_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 of 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"
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (10)

Collapse
 
gijovarghese profile image
Gijo Varghese

There is a much better way to set environment variables in NodeJS. I've written a detailed post - How I Setup Environment Variables in NodeJS. No, it’s not “dotenv”

Collapse
 
jdmedlock profile image
Jim Medlock

Thanks for this Gijo. I’ll read your post and will research. I typically place my .env’s in an encrypted password vault, but sharing changes with team members an making them available to new team members can be cumbersome. Thanks for showing an alternative method.

Collapse
 
gijovarghese profile image
Gijo Varghese

Thanks. Check it out and give me your feedback

Thread Thread
 
jdmedlock profile image
Jim Medlock

I've taken a look at your article and your approach has merit. I'm still gathering my thoughts on this, but as of this moment I still prefer an approach (independent from any particular .env package) of documenting the names of the environment variables in the project readme.md, but still excluding the .env file from the project.

However, something I need to explore is setting up .env files unique to each environment. In dotenv this can be done by customizing the path in the call to the config function. For example .env.production, .env.staging, .env.development, etc.

Having said this though, there may be an opportunity to simplify this by creating a wrapper package to do this outside of application code.

This is a good topic for discussion and I'm interested in any additional comments and suggestions you might have.

Thread Thread
 
gijovarghese profile image
Gijo Varghese

For me, that one works prefectly fine. Like I can commit my .env files, colleagues can override it using .env.local, set environment specific variables like .env.staging.

Initially, as you said I updated everything in the readme and added .env to .gitignore. However, I tell everyone that there is a new variable in the readme. Most of the time front-end devs come to us and says "this thing doesn't start!". Me "pls update env file, run migration and try again. If not I'll come"

But if you add .env from .gitignore and add everything in readme, then what's the point of it? Someone who got access to your git repo just need that readme right?

Thread Thread
 
jdmedlock profile image
Jim Medlock

I don't add the environment variable values to the readme. Only the names, a description, and a sample value (not the real value).

I understand the downsides, and I'm revisiting my use of environment variables because there are downsides as you've pointed out. Using an encrypted vault for secrets like I'm currently doing still means new devs need help setting things up.

Thread Thread
 
gijovarghese profile image
Gijo Varghese

ok got it. Could pls explain bit more about "vault of secrets", how does it work? where do you store it?

Thread Thread
 
jdmedlock profile image
Jim Medlock

I use 1Password, which is a commercially available password keeper, to store information, not just about my personal accounts but also to keep information about the projects I participate in.

There are quite a few different products that do this. The important thing is to pick one that's encrypted, easy to use, and works well on your OS.

Some teams I've worked on use a vault like this with shared credentials.

Thread Thread
 
gijovarghese profile image
Gijo Varghese

Nice! Thanks for the info

Thread Thread
 
jdmedlock profile image
Jim Medlock

Anytime! This has been a great discussion and its making me rethink what I'd previously taken for granted. Thank you!