Environment Variables in Node

2022-01-11

Environment variables are variables that you can set that are hidden away and exist as part of the environment. These variables allow for different behavior depending on where the application is running. You can set production environment variables and development environment variables and then without changing a line of code in an application, run in both environments correctly.

This extremely useful. When you ssh into a server, there are often many environment variables already set. For example if you do "echo $PATH" in a terminal, this will give you the executable search path. "echo $TERM" will tell you the environment's term type.

These are system environment variables and we could use them with out application. We can do something like 'export APPLICATION_ENV="DEV"' to set our application environment and then in our application we would read these environment variables in and trigger different behavior based on where we are. This would require us to make sure these environment variables are always set and we would then need to add this to somewhere like the /etc/profile file or ~/.bashrc. This would work but then multiple application environment variables would all be in the same place and we might run into all sorts of issues if we make a mistake in setting variables.

Instead the better solution would be to have application specific environment variables. We want each application to have only the environment variables that it needs, set. We can write our own custom code to do this or we can rely on a library. Ideally, this would be part of the node standard library but we don't live in an ideal world.

I use the dotenv library as it's a very simple library with no dependencies and the code is full readable. Before we get to the library however, I'll go over how I would add environment variables.

Environment variables in node are exposed via the process.env variable.

In a plain javascript file, we can do the following:

console.log(process.env);

This will output an object of all the environment variables that are currently set.

What we want to do is to add our own environment variables to this object.

process.env["APPLICATION_ENV"] = "DEV";
console.log(process.env);

Now we should see our APPLICATION_ENV variable being set. This will only be while the application runs, this environment variable isn't actually getting exported into the system so there is no pollution.

Now armed with this logic, we can write a .env file that we read in and set environment variables with. We use a .env file because we want it to be hidden most of the time. We also need to make sure to add this file to the gitignore so that it doesn't get added to the repo. We don't want our secrets getting leaked.

Write a .env file like the one below.

APPLICATION_ENV="DEV"
SECRET="super-secret-key"
DATABASE_USERNAME="username"
DATABASE_PASSWORD="password"

This is a very simple set of key value pairs that we want to read in.

Now we can write a quick and dirty script to read in a file and load these variables into the process.env.

const fs = require("fs");
const lines = fs.readFileSync(".env").toString();

for (let line of lines.split("\n")) {
    if (line === "") continue;

    const tokens = line.split("=");
    const key = tokens[0];
    const value = tokens[1].replaceAll('"', '');

    process.env[key] = value;
}

console.log(process.env);

We can read in the file, split it into the lines and then split again on the equals sign. We then do a bit of clean up on the values to get rid of any quotes and there we have it. A very quick way of loading environment variables. This is the core idea of the dotenv library.

Now we can do things like:

if (process.env.APPLICATION_ENV === 'DEV') {
} else {
}

I'm ignoring things like single quotes vs double quotes, newlines being more than just "n" and probably a whole host of other things. You can download the dotenv library and look inside lib/main.js to see how it works and it will be similar in essence.

The way to use dotenv is to first install it.

> npm install dotenv

Then all we need to do is require the dotenv and run the config function.

We also want the environment variables loaded as one of the first things in our application so it should go at the earliest possible location.

require("dotenv").config();

With that, something akin to the script we wrote will run and load the process.env with all of the variables from the .env file.

Environment variables are pretty useful and straightforward to use!