Environment Management In Node.js
A guide to managing multiple configuration environments and variables throughout the development process.
Table of contents
Assalam u Alaikum & Hello Everyone! ๐
Hope you all are good โจ๐ค
Introduction
Environment management is an essential component in backend development. Storing database credentials, third-party API keys and much more confidential information is important for security reasons.
There are a lot of techniques to manage the environment configurations. The most common way of managing this is via using Environment Variables. There are also a lot of third-party tools and vaults that help you to store confidential information.
In this post, we will learn how to manage different environments(production, stage, and development) in Node.js.
Let's start ๐
Creating A Node Project
Let's create a simple node application by using npm init -y
$ npm init -y
npm init
is going to generate a package.json
file for our project. And the -y
flag is simply saying yes to all the steps in creating package.json
file.
Here are the contents of package.json
:
{
"name": "environment-management",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
You can modify the other fields as per need. For this post, the most important section of the package.json
file is scripts
.
Introduction to Environment Variables
An environment variable is a variable whose value is set outside the program, typically through a functionality built into the operating system or microservice. An environment variable is made up of a name/value pair, and any number may be created and available for reference at a point in time.
Structure Of Environment Variables
We create environment variables in name/value pair like:
VARIABLE=VALUE
Default Configuration Support Using Node.js
In Node.js, you can pass the environment variables when you are running any javascript file using node.
This is the default behavior of how the node binds the variables to the process.env
object. By default, there are a lot of environment variables defined.
Let's define our first Environment Variable using the command interface.
Let's create a file app.js
at the root of your project and run it using node.
> app.js
console.log('Welcome to configuration management using Node.js');
Now, we can run the application using:
$ node app.js
Output:
Welcome to configuration management using Node.js
Now, let's pass an environment variable via command interface and run the file something like this:
$ GREETINGS=Hi node app.js
Now, node will understand that there is an environment variable GREETINGS
of which the value is Hi
and I have to bind this variable with the process.env
object.
If we console the process.env.GREETINGS
in our app.js file like this:
console.log(`${process.env.GREETINGS}, Welcome to configuration management using Node.js`);
The output will be:
Hi, Welcome to configuration management using Node.js
The above method of passing environment variables is fine but it's not scalable.
Environment Management Using dotenv
dotenv is one of the most highly used package to manage environment variables.
It's very rich in functionality and supports loading environment files with paths.
Working with dotenv
Let's install dotenv
by simply running
$ npm install dotenv
After the installation, create a .env
file at the root of your project. dotenv
will automatically pick this file and read the environment variables from it. After reading, it will assign all the environment variables to the process.env
object.
Let's create a .env
file and write a simple environment variable in it.
GREETINGS=Hello World!
To use dotenv
in our node application, import the package at the root of the project or the entry point of your server then call the config
method of it.
console.log(process.env.GREETINGS); // undefined (dotenv is not loaded)
require('dotenv').config(); // load dotenv, all the environment variables are loaded now.
console.log(process.env.GREETINGS); // Hello World!
In this way, we can add as many environment variables into the .env
file as we want. All of them will be parsed by dotenv
.
Multiple Environments Using dotenv
We have looked into how dotenv
work, and how it loads the environment variables. But this is ideal when you have only one environment to work with.
In real-world projects, we have multiple environments like production, stage, QA and development, etc.
To handle this use case, we can use the path
option in the config
method.
Let's create three different files for the production, stage, and development environment. We can add a simple variable of CONTEXT
in it.
$ touch .env.production
.evn.production
CONTEXT="production"
$ touch .env.stage
.evn.stage
CONTEXT="stage"
$ touch .env.development
.evn.development
CONTEXT="development"
Now we have environment variable files for every environment, we can load them according to the environment.
Let's load the development environment files. We can use the same traditional command pattern to provide our node app in the context of the development environment.
Let's modify the script
section of the package.json
file as:
"scripts": {
"start:dev": "ENVIRONMENT=development node app.js",
"start:prod": "ENVIRONMENT=production node app.js",
"start:stage": "ENVIRONMENT=stage node app.js"
}
We have used the default method of loading environment variables to pass the environment context to our node app.
Now in the app.js
file, we can create a small function that can return the environment file path based on the ENVIRONMENT
variable passed in the command line. Note that the path
option takes the absolute path of the environment file, not the relative. That's why we can use the __dirname
to get the absolute path.
function getEnvironmentPath() {
const environment = process.env.ENVIRONMENT;
const environmentMap = {
production: '.env.production',
stage: '.env.stage',
development: '.env.development'
};
return `${__dirname}/${environmentMap[environment] || '.env'}`;
}
Now we can call the getEnvironmentPath
function within the config
function of the dotenv
.
require('dotenv').config({ path: getEnvironmentPath() });
console.log('Context: ', process.env.CONTEXT);
Let's test our code by running the respective commands.
$ npm run start:dev
Context: development
$ npm run start:prod
Context: production
$ npm run start:stage
Context: stage
Boom ๐ฅ We are done! ๐ฅณ
Let' me know in the comments about how you manage your environments in nodejs ๐
Conclusion
It's very important to note that managing environments is a vast topic. This post is a simple guideline for engineers on how they can manage different environments during the development phase of the product.
In many cases, dotenv
is not recommended in production deployments. You can always tweak the configuration according to it. The goal is to enable you to take effective decisions and understand how environmental management works.
That's it, folks! Thank you! โจ