Environment variables in Node.js. The Right way!

numtostr profile image Vikas Raj Originally published at vikasraj.dev ・4 min read

This is my first blog post. Hope y'all like it 🤞.

Environment variables are very fundamental part of developing with Node.js or any server side language. They always contains very sensitive data and doesn't meant to be shared with outside world. You have to make sure that your server is properly configured to make use of correct variables for both development and production environments. Any mistake can lead your server to crash.

Working with environment variable in Node.js is very easy and simple. This post will walk you through the different approaches of using environment variables in Node.js.

If you want to learn about environment variables in Node.js in click here.

1. Using package.json

You are thinking how? But you can pass key=value pairs directly in the npm scripts in the package.json. This is a valid a place to put environment variable but not a secure place.

Below is an example of setting Node.js execution environment to production on the npm's start script.

NOTE: This will probably won't work with Windows OS.

    "scripts": {
        "start": "NODE_ENV=production node bin/www.js"

I also use this approach when I work with debug module or setting the Node.js execution environemnt. Some points to be considered when using this approach:

  • Shouldn't not put any sensitive data in here as it is visible to everyone and you can't ignore package.json in the .gitignore.
  • Don't put more than 2 variables as it could quickly becomes a mess.
  • Separating variables for development and production environment can be very hard.

2. Different keys files

This approach is completely different from first approach and addresses some of the issue of first approach.

Instead of using package.json, we can use keys.dev.js for development and keys.prod.js for production environment. Each file stores different variables for different environment.

Make sure to ignore keys.dev.js in the .gitignore before commiting any changes.

But how we can use them?

Using them can be tricky (when i initially got frustrated), So pay attention. Both files are exported by a third file i.e keys.js which checks the Node.js execution environement i.e. NODE_ENV and exports the correct keys file.

But how can I check Node.js environment before running our code? Look at the first approch for a brief. Below is an example for this approach:

// keys.dev.js ==========
module.exports = {
    PORT: 5000,

// keys.prod.js ==========
module.exports = {
    PORT: process.env.PORT,

// keys.js ==========
const devKeys = require("keys.dev.js");
const prodKeys = require("keys.prod.js");

if (process.env.NODE_ENV === "production") {
    module.exports = prodKeys;
} else {
    module.exports = devKeys;

This approach addresses all the issues of the first approach.

  • Multiple variables can be easily managed.
  • Development and production both have their own keys.
  • Development keys i.e. keys.dev.js can be ignored in the .gitignore keeping secrets away from others.

But no one wants to maintain extra code/file (including me). There must be a better way to do this!

3. .env comes to the rescue

.env is a special file which is used to define environment variables in Node.js. It holds key=value pairs to define the variables.

Make sure to ignore .env in the .gitignore before commiting any changes.

But, Node.js doesn't know how to read and parse this file. How do we do that? Either you could write your own logic to read and parse the file or Use a third party module to do the heavy lifting.

One popular module is dotenv (which i use) which can guide through the basic of .env file.

~ Creating the file

First create a file with name .env in the root of the project which contains all variable which will be injected in the environment by the dotenv.

# .env ======
WHO_AM_I="Who Knows"

~ Configuring the dotenv

First intall the dotenv package from the npm as a dev dependencies as we don't need this in production.

npm i -D dotenv

There are several methods to load dotenv package. But, I will show you the method that i like.

To load the dotenv package and correctly read the .env file you have to modify the scripts in the package.json. Like below

    "scripts": {
        "start": "node bin/www.js",
        "dev": "node -r dotenv/config bin/www.js"
        // For nodemon users ====
        // "dev": "nodemon -r dotenv/config bin/www.js"

As you can see there are two scripts

  • start for the production
  • dev for the development which loads the dotenv module

This will make sure that we don't accidentally load the dotenv in production.

~ Run the code

Now you can run the server but typing the following command.

npm run dev

And BOOM! You can now use all the variables defined in the .env file by the following syntax.


So What kind of magic is this? What is going on? In the dev script we are telling node to preload a module by passing -r <module_name> flag. By requiring dotenv/config which read and parse the .env and sets the variable in the environment and provide access to those variable before running our code.

Now, we have a single place to define all the environment variables.

To make life a little easier, you can make a separate file i.e keys.js which exports all those variable like so we do in the second approach. This helps us to organize all the variables we use in our code.

NOTE: If you add variables in the .env then also update your exports in keys.js file.

// keys.js ======
module.exports = {
    PORT: process.env.PORT,
    WHO_AM_I: process.env.WHO_AM_I,

Points to be considered

  • Always ignore your development keys in the .gitignore.
  • Don't mess with NODE_ENV variable. Values other than development or production can break your app.
  • Always restart your app after changing environment variables.

Posted on by:

numtostr profile

Vikas Raj


Full Stack Engineer who is always lazy to find big solution. DevOps is in the pipeline. Loves guitar 🎸.


markdown guide

Another option is to use command line arguments like node app.js --option argument which prevents any keys to ever be committed or written to a file since they are only made available at run-time.

It also plays nice with docker since one can just write any secrets into a docker .env file that feeds ENV variables either to a container or an image.


why your script start prodouction did not load dotenv?


dotenv is a dev dependency. In production, keys are stored on the server which can be accessed by node without using dotenv


can you give me little example how storing keys on production? thank you

For example, heroku has a option in app settings to enter environment variable.

yes, in heroku there is an option to store that. but how if we use own server?

Like this

// keys.js ======
module.exports = {
    PORT: process.env.PORT,
    WHO_AM_I: process.env.WHO_AM_I,

My script is currently

"serve": "vue-cli-service serve"

I tried adding "node -r dotenv/config vue-cli-service serve"

I ran that but then it failed to compile

    throw err;

Error: Cannot find module './front/vue-cli-service'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:793:17)
    at Function.Module._load (internal/modules/cjs/loader.js:686:27)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1043:10)
    at internal/main/run_main_module.js:17:11 {
  requireStack: []
npm ERR! errno 1
npm ERR! front@0.1.0 serve: `node -r dotenv/config vue-cli-service serve`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the front@0.1.0 serve script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.


Ok, but how to manage the .env files? Where to keep them? How to share them? For example how do I share a .env.development file with another developer since the file is not in the source control?


You can include .env.development in the source control. But make sure It doesn't contain any actual keys. Because thats the whole point of secret.

The way I do is I make a .env.sample file with all the env but without any secret or api keys.