Environment Variables in Node.js

Many web applications have configurable elements to them. For example, if my application communicates with a database, it needs to know certain information (such as the database host, port, password, and so on) to connect to that database.

How should I store the database connection information? I could hard code it into my application's code, but that causes at least two problems.

First, the database credentials would be visible in the codebase, which is a security issue.

Second, it would also reduce the application's flexibility and portability. For example, if I wanted to change my application's host, I'd have to update its codebase with any new database credentials before I could re-deploy it.

Fortunately, you can avoid hard coded config values by using environment variables. Here I'm going to show you how you can use them in Node.js.

Environment variables basics

Environment variables are basically in-memory variables that can influence the way a program runs. They aren't a Node.js thing specifically, instead they exist as a construct in your operating system.

Take this Node.js program that runs a web server:

const http = require('http');

const app = new http.Server();
const port = '8000';

app.on('request', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.write('Hello World');
  res.end('\n');
});

app.listen(port, () => {
  console.log(`App is listening on port ${port}`);
});

If you save this as index.js and run node index.js in your terminal, you'll see App is listening on port 8000 printed out (assuming port 8000 is available).

Here, the port number is hard coded. This might be fine for running the app locally for development, but for production this is likely to be a problem.

For example, apps deployed to Heroku are assigned port numbers dynamically. If Heroku assigns port 8238 for your app, only for your code to try and bind itself on port 8000, your app won't work.

This is where environment variables come in. Hosts like Heroku set an environment variable called PORT which your app can read to discover its designated port number. Let's set and read this PORT environment variable ourselves.

First, change the port variable declaration code to this:

const port = process.env.PORT || '8000';

In Node.js, all environment variables available to a program can be read from the global process.env object. We want the value of the PORT environment variable, so we need to read process.env.PORT.

And now run the program using this command:

PORT=8001 node index.js

Now you should see App is listening on port 8001 printed in the terminal.

Immediately before any shell command you can specify a list of environment variables you want to set for the process you're about to run. We just set PORT=8001 but if we had more things to configure for this application, we could specify multiple environment variables, for example:

PORT=8001 FOO=bar MESSAGE='Hello world' node index.js

Also, if you're wondering why PORT is capitalized, it's just a very common convention for naming environment variables. People almost always write environment variables in capital letters, with underscores separating worlds - for example, DATABASE_LOGIN_NAME.

Anyway, I hope you can see how we've improved the application's flexibility. The application doesn't make any assumptions about what port number it should run on - instead, it can discover that from its environment.

NODE_ENV

NODE_ENV is a special environment variable that you set based on what type of environment your application is running in. It's a convention, but one you should absolutely abide by.

If you are running your project locally for development, set NODE_ENV to development. The way different Node.js projects are setup can differ massively, but many work such that in development mode you get some helpful features turned on such as debug logging.

For instances of your application running in production, set NODE_ENV to production. This will help ensure the best performance for your app - for example, many libraries such as express make various code optimizations that do not take effect unless NODE_ENV is production.

In addition to development and production, some projects may use other values for NODE_ENV in some circumstances. The most common example is for automated testing, where most people set NODE_ENV to test.

Dotenv

Some people use a module called dotenv to set environment variables in their project.

Basically, you specify a list of environment variables in a file called .env. Dotnev takes this file and, under the hood, loads the environment variables you set into the process.env object.

It's a tool of convenience more than anything. You don't want to use it for production, but it can be useful in development mode. For example, you could use some code like this, to conditionally load your .env file in non-production environments:

if (process.env.NODE_ENV !== 'production') {
  require('dotenv').load();
}

Whether you commit your .env to source control is up to you. As long as you're using it just for development purposes, and you aren't storing sensitive credentials in your .env file, I don't see any harm in it.

Further reading

The more familiar you are with UNIX/UNIX-like environments, the easier you will find your life as a developer. You'll understand topics like environment variables no problem.

A book I always reccommend reading is The Unix Programming Environment.

Conclusion

Environment variables are essential to creating professional web apps. Using them improves portability and flexibility for your application. Your application won't be tightly coupled to one environment, but instead can read environment variables to discover things about the environment it is living in.