Skip to content

Set up and Test a Dot Env (.env) File in Node

Using the dotenv package, we can add a .env file to our Node.js project to serve as a central place to manage and document environment variables. This makes them easier to update, maintain, and perhaps most importantly, to discover. Environment variables allow you to run your Node.js app anywhere.

In this tutorial we’ll:

  • Create an .env file for our local environment and populate it with some key value pairs
  • Use the dotenv package to read and parse the .env file and access the data it contains via the process.env global
  • Provide some additional thoughts about best practices for managing and documenting environment variables in Node with .env files

By the end of this tutorial, you should be able to use a .env file in combination with the dotenv package to effectively manage environment-specific configuration for your application.

Goal

Set up and test an .env file with your environment variables.

Prerequisites

Watch

Why use dotenv?

Once you’ve got more than a couple of environment variables to worry about, managing them can become somewhat cumbersome.

  • Where do you find a list of what variables need to be set?
  • How do you keep track of the values you want to set them to?
  • Typing them at the command line every time you execute your code is error prone and tedious

By far the most common solution to this in the Node.js world is the dotenv library. This allows you to create an .env file in the root directory of your codebase which contains key/value pairs defining environment variables. This file is read by the dotenv library and appended to the process.env global.

Do not commit your .env file

We recommend that you never commit your .env file to version control. It should only ever contain environment-specific values. In order to ensure you don’t commit it, go ahead and update your project’s .gitignore file before doing anything else.

Edit your _.gitignore_ file

Edit your project’s .gitignore file and add a line for .env:

.gitignore

# Ignore .env files.
.env

Commit it

Terminal window
git add .gitignore
git commit -m "Adding .env to .gitignore rules."

Add a .env file and parse it with dotenv

Next, we’ll install the dotenv package, create a .env file for our project, and read the data it contains at runtime.

Install the dotenv package

Start by installing the dotenv package:

Terminal window
npm install dotenv

Create a new _.env_ file

Then create a new .env file for your project. This file will normally live in the root directory of your project.

Remember, this file should not be committed to version control. But, we do need one present in this environment where we’re doing development.

Example:

Terminal window
touch .env

Add variables to the _.env_ file

Open the new .env file in your editor of choice and add your variables. The syntax for this is VARIABLE_NAME=value, with one entry per line.

Example .env:

# Enable debugging mode.
DEBUG=false
# API connection settings.
API_HOST=https://jsonplaceholder.typicode.com
API_KEY=heynode
API_SECRET=hey-this-is-a-secret

Read, and use, the _.env_ files contents

As early in the code as possible, require dotenv and call the config() method. Usually that’s in the main index.js file or other primary entry point of the application.

Example index.js:

require('dotenv').config();
console.log(process.env.API_HOST);
const response = await fetch(`${process.env.API_HOST}/users`);

In the above code the require('dotenv').config(); is doing a lot of work for us. Calling config() will read the .env file assuming it’s in the same directory as our index.js file, parse its contents, and assign them to process.env.

Run the code

Finally, run the index.js code without passing any variables in the command.

Example:

Terminal window
node index.js

Notice that the log message outputs the value of API_HOST as defined in our .env file.

Now you can consume the contents of an environment-specific .env file via the process.env global anywhere in your application. However, there’s more we can do to improve the experience.

Optionally create a config.js module

For applications with more than a couple of configuration options we recommend creating a config.js module. We can use it as a central place to gather environment variables, as well as other application configuration that we want to live in code. Unlike the .env file, this one should absolutely get committed to version control.

Example config.js:

const dotenv = require('dotenv');
dotenv.config();
module.exports = {
version: '1.0.0,
canonical_url: process.env.APPLICATION_ROOT,
api: {
host: process.env.API_HOST,
key: process.env.API_KEY,
secret: process.env.API_SECRET,
timeout: 30,
}
enabledPlugins: [
'plugin-one',
'another-plugin',
],
};

This example mixes together configuration from our .env file allowing things like the API_KEY to remain specific to an environment, while also allowing us to mix in configuration values that we want to apply to all instances of the application.

Doing it this way also has the benefit of allowing us to import configuration wherever it’s needed, and use destructuring to pull out only the values we need. This can help keep code cleaner.

Example updated index.js:

const { version, api } = require('./config');
console.log(`Running version ${version}`);
const response = await fetch(`${api.host}/users`);

Document your application with an .env.example file

While the .env file itself should be environment specific and not in version control you can create an .env.example file that documents the necessary variables and commit that to your codebase. This provides a useful reference for anyone executing the application without requiring them to dig through the code to figure out which variables they need to set.

We recommend creating an .env.example file in the same directory the .env file should exist, and populating it with fake values.

Example .env.example:

# Environment variables.
# Base URL of the API server to use. No trailing slash.
API_HOST=https://example.com
# API access credentials.
API_KEY=key
API_SECRET=secret
# Enable debug mode (true) or disable it (false).
DEBUG=false

What happens to environment variables that already exist?

The dotenv library will never modify any environment variables that have already been set. In particular, if there is a variable in your .env file which collides with one that already exists in your environment, then that variable will be skipped. This allows you to override all .env configurations with a machine-specific environment, although it is not recommended.

You probably don’t need dotenv as a runtime dependency, and instead can install it with npm i dotenv --save-dev. If you do so, make sure you have a a PRODUCTION flag set when running in production to help you decide whether or not to attempt to load dotenv. In production you likely don’t have your dev dependencies installed, and trying to run dotenv when it’s not installed will result in a crash.

if (!process.env.PRODUCTION) {
require('dotenv').config()
}

Recap

In this tutorial we installed the dotenv package from npm, and used it to read in a .env file containing environment variables for our application, thus allowing for a clean separation of environment-specific configuration from our application’s code. We then looked at creating documentation for the environment variables our application depends on in an .env.example file. Finally, as a reminder, we recommend that you never commit your .env files to version control.

Further your understanding

  • Do any of your existing projects use environment variables? How are they managed? How are they documented?
  • What configuration from your codebase do you think should be added to a .env file?
  • Learn about preloading environment variables which allows you to remove dotenv as a runtime dependency

Additional resources