Skip to content

Instantly share code, notes, and snippets.

@SgiobairOg
Created December 8, 2020 21:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SgiobairOg/e39778b1d5c0d3847553b64f0ae83e26 to your computer and use it in GitHub Desktop.
Save SgiobairOg/e39778b1d5c0d3847553b64f0ae83e26 to your computer and use it in GitHub Desktop.
title published description tags cover_image
Hoarding Secrets in NodeJS
false
An introductory guide to using .Env files in local development and deployments. Suitable for beginners and up.
NodeJS, Git

When working with NodeJS it's likely you'll wind up with a few secrets; not secrets like "Popeyes has the best fries" but API keys, encryption keys, database passwords, and other things you wouldn't want anyone else to learn.

These secrets are values you'll need to use in your code, but you never want to directly include them in the actual code, especially when you code is distributed to users, or if you use public repositories for your code. So how do we run these values in our code? Let's learn a bit about Environment Variables.

If you're already comfortable with environment variables you can jump right to implementation.

The Execution Environment

When you run code on a computer, whether your physical machine, or a virtual machine in the cloud, the operating system and configured software on that machine are your code's execution environment. When running Node scripts, information about the execution environment is made available through special variables. We call these environment variables.

On a physical server you might set these values directly in the operating system. If you're deploying code to a cloud environment there will usually be a preferences pane where you can set your environment variables, some are even set for you. But what about local development? You could set them in your system settings, your dev computer is, after all, an Execution Environment in itself. Setting the variables directly like this is tedious, however and can quickly get out of hand when you work on multiple projects.

There's a better, more flexible way; lets learn about…

.Env Files

A .env file is a text file that lives on your machine where you define key-value pairs. This file is then loaded by the Node runtime and the values are made available as environment variables. You can have a file for each project so you don't have to worry about mixing secrets or changing system variables when you change project. When you need a new variable you add it to the file and rerun your script.

Getting started won't take much but, these are your secrets we're talking about. You can, very easily, accidentally share them with the world. There are plenty of stories in dev and security circles of sites with publicly published encryption keys, admin log-ons, etc. proper care should be taken from the start.

So with that in mind…

Let's Use Some Secrets

Create a new folder for your project as you normally would. For me this means running the following in my terminal:

https://gist.github.com/879eaa54726970352342c1af7edb92c5

If you want to follow along with my code, you can clone the repository from Github. You'll just need to add your own .env file. No promises, but I may even add more examples as they come up.

Next is a very important file for guarding our secrets. Whether you use Git or not it's a good habit to create this file before you add a single secret to your project. This helps to prevent you or someone else who works on your project from sharing your secrets in public and preserving them in your project's Git history. If you're not familiar with Git, I'm talking about the Git Ignore file. This file tells Git – if it's ever initialised in your project – to ignore specific files. In this case we want Git to ignore our .env files.

Create the file .gitignore in the root of your new project and add the following content:

https://gist.github.com/9ecf389c10028d8e6aeec59ce64319a5

With that file saved in our root we're now safeguarded against the very common accident of committing our secrets to a code repository, we've even covered several formats for the file (though it's recommended to only ever have the one .env file). With that peace of mind we can set up and start using our .env files.

Using the DotEnv Package

I mentioned before that the .env file needs to be loaded into the node runtime. To do that I use a package named DotEnv. DotEnv is a well-maintained module that will load our .env file environment variables and make them available to our node process.

Install DotEnv as a Dev dependency now using either npm (what I'm using if you're following me) or yarn.

https://gist.github.com/8e6b5bbab0d161de674fadb7b6f70bb4

Now we need to set up our Node package script so that DotEnv is run and parses our environment variables. Open your package.json file and modify your scripts to look like this:

https://gist.github.com/a2d52355c14f95257b16f7e8c594fec6

What we're doing in our 'dev' script is pre-loading dotenv and running the config before node runs our entry script. I prefer this method because we don't need to add any code that will only be used on our dev environment. Remember, these environment variables will be set by other means on our deployment environments; we only need DotEnv for our local development.

Creating our .Env File

With our .gitignore insurance plan in place and DotEnv ready to use, let's create our .env file and share some secrets.

Create the file named .env in your project root and add some secrets like so:

https://gist.github.com/84e9983b333d0e72b2fe7f775297d806

You can use string values directly, single quotes, double quotes, etc. For more on how the file is parsed you should review the parsing rules. You can have multi-line values, whitespace, empty rules, and a lot more.

With our .env file created now we can use these super-secret keys in our code.

Retrieve our Secrets

Let's create our 'index.js' file and read our variables in. Create the file and add the following code passages. We'll explain each one as we go.

https://gist.github.com/4a05427b1b13dfd7f0f782288e395af8

No secrets slipping out here in the code but run npm run devand the truth is revealed.

https://gist.github.com/aaa0d112f738616878531b3787ecab32

So what happened here? DotEnv ran before our script and loaded all of our key-value pairs into an object set to the env property of our Node process. In the first line we access our chosen key and assign it to a constant. We now have the value of our secret ready to write out (just for our demonstration) without having to reveal it in our code. It stays safely in our .env file.

Let's add some more. Since process.env is an object, we can use destructured assignment to access a couple of our environment variables at once like so:

https://gist.github.com/0feb228182fa9fd712f67a8466951ddb

Run npm run dev again and 'Ta-Da' we've aired more of our secrets out in the open.

One more example: what if we need a default value for one of our environment files? A common example is the PORT value. On cloud environments especially the PORT that your script runs on is decided by the environment and the PORT value is set for you but if it's not, you still want a port number for your process to listen on. Let's do that like so:

https://gist.github.com/6a66fb2159ab742000bedbc36996402b

This is known as 'short-circuit evaluation' we're using the logical 'OR' operator to assign the second operand if and only if the first is null (or falsy). So if we set the PORT .env variable, which we have, our script will report that it is listening on that value, 8081 in our case. If we didn't set the value, it would listen on 3000 instead.

That's it for the basics.

So, When Would I Use This?

When you're working in Node there are a lot of places you'll encounter a need for secrets.

Databases

To connect to a Database you'll need to provide a user, password, and a database name at a minimum. No one needs to know what those values are and having them get accidentally published to your public repository or out there in plain text on your server could be a disaster.

API Keys

Many times when we want to use an API for our development we will have an account and keys from the API provider. The API may limit our rates or we may be charged by usage. Anyone with our keys can make their own requests as us! Publishing API keys is a really good way to rack up a huge bill with your API provider. Even worse - the API could have full access to sensitive information like your collection of heirloom dad-jokes.

Environment Configuration

When you deploy your code to the cloud certain environment variables are going to be provided for you. PORT is a common example. Your provider will usually document what variables they make available for you and will also give you space to set your own. If you can set a default for these you don't really need to add them to your local .env file (reduce the risk) but now you know how to set them if you need them.

Configuration Without a Release

Not everything in your .env has to be a secret. One neat use for your environment variables is being able to change a setting in your code without needing to deploy a new version to your server. For example you could add an on/off switch for the new feature you're about to release. The code is deployed but with a flag tied to an environment variable. When marketing sends out the email to users you don't need to start a deployment, you just set your switch in your server environment and your new feature comes to life.

There are a lot of things that can be done here (but beware of the fact that you're not able to provide for version control on these settings).

That's It

So, first, I'm going to say it again. Before adding a .env file and any secrets add a .gitignore file with .env. Even if you don't use git initially. Protect yourself from making the same mistake all the rest of us have.

I hope you've got an overview of how to use Environment Variables in Node. There's a lot more out there you can dig into, the DotEnv README is a good start, but this should be enough to get you started and give you some basic protection against broadcasting your secrets to the world.

Recapping the Steps

  • Add a .gitignore file to your project root with a rule to ignore '.env' at a minimum

  • Install DotEnv (or a similar module) as a dev dependency and add the preloader to your package script

  • Add your secrets as key-value pairs in your .env file

Your environment variables are waiting in process.env. Good luck, and happy developing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment