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 ======
PORT=5000
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.
process.env.YOUR_VARIABLE_NAME;
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.
Top comments (19)
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 usingdotenv
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
ok, thank you.
But, still we're using 'process.env', which uses to 'dotenv' package in production?
In production I didn't use .env, I store all credentials on host environment or if I use docker, I store it in docker secrets.
Cool, thanks!
Yes thats right that we store
keys
on server.But what about the dotenv package we imported into file and written as
process.end.VAR_NAME
? Wont it require to usedotenv
package?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
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 feedsENV
variables either to a container or an image.If I used the same server for development and production, I'd have to not install dotenv as a dev dependency and not use server variables, just 2 different .env files, one for each prod/dev application folder.
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.
What do you mean when you say it doesn't contain actual keys? How did the other developer run test like i did on my local when it doesn't contain actual keys?
Thanks, very useful
great approach. How do i add a npm script for another environment (i.e, staging environment)?