DEV Community

Cover image for Environment Variables in NodeJs: The complete Guide
alakkadshaw
alakkadshaw

Posted on • Edited on • Originally published at deadsimplechat.com

Environment Variables in NodeJs: The complete Guide

In this article we are going to cover environment variables also known as env variables. These are basically key-value pair of data set that is stored in the operating system level

In this article we will learn about environment variables in NodeJs with examples

  • What are Environment variables
  • Why are env variables are important
  • Prerequisites for the project
  • Installing NodeJs and setting up a new Project
  • Initializing your first env variable with NodeJS
  • env variables in API calling / async tasks
  • env variables in Database operations / async tasks
  • Advanced env variable manipulation in Node JS: Encoding, Validation and type conversion
  • Secrets management and Security Best Practices with examples
  • Common Pitfalls and how to avoid them
  • Conclusion

Environment Variables

Environment variables are a key-value pair data set that are avaiable at the operating system level. These data set are available in all major operating system command line shells Windows, Mac and Linux

Why are env variable important?

  • Separation of concerns
  • Security
  • Portability
  • Scalability
  • Compatibility
  • Interoperability

Prerequisites for this Project

In this project we are going to assume the following

  1. Basic knowledge of NodeJs
  2. Basic Knowledge of JavaScript
  3. Some knowledge of Backend development

Installing Node and setting up the project

These are different methods of installing NodeJS on your machine. You can go to the node official website and download a version from there

Let us consider installing nodejs in a linux operating system preferably ubuntu

Step 1: Open your terminal

Step 2: Use curl or wget script below to install nvm using the nvm git repository. run the script to install the nvm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
# OR
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
Enter fullscreen mode Exit fullscreen mode

Step 3: Close and re-open your terminal and run the following to apply the script

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
Enter fullscreen mode Exit fullscreen mode

Step 4: Confirm Installation

You can confirm that NVM has been installed on your system by running the following command

nvm --version
Enter fullscreen mode Exit fullscreen mode

*Step 5 : * Installing Node

type the below command to install the latest version of node

nvm install latest
Enter fullscreen mode Exit fullscreen mode

or

nvm install --lts
Enter fullscreen mode Exit fullscreen mode

Step 6 Setting the default version

you can set a default version of node to be used across your system by using the following command

nvm alias default 20.8.0
Enter fullscreen mode Exit fullscreen mode

Step 6: Checking if node is installed

you can type the below command to check if the node is installed on your system

node -v # tells you the current version of node on your system. In our case it is 20.8.0
Enter fullscreen mode Exit fullscreen mode

Initializing the project

Now, that we have installed the node on our machine. Let us create a new project where we will be using the env variable

create a new directory and name it env-demo and cd into the directory

mkdir env-demo
cd env-demo
Enter fullscreen mode Exit fullscreen mode

Now, that we have created the folder and cd into it. Type the below command to initialize a new project and fill out the fields as you see fit.

npm init
Enter fullscreen mode Exit fullscreen mode
package name: (env-demo) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
Enter fullscreen mode Exit fullscreen mode

this will create a package.json file for you that looks something like this:

{
  "name": "env-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Initializing your first env variable: Step By Step

Step 1: Install the dotenv package

Now that you have initialized the project in the above section and created a package.json file. In your terminal type the below command to install the node dotenv package that is used to create env files

npm install dotenv --save
Enter fullscreen mode Exit fullscreen mode

This will install the dotenv package and save it as a dependency in your package.json file. that should look something like this

{
  "name": "env-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.3.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2 Create the .env file

  • In your root folder create a new file and name it .env.
  • Open your .env file in your text editor and create some key-value pairs. These are your environment variable. I have created some for your reference below
DATABASE_URL=sample-database-url
PORT=3000
SECRET_KEY=mysecretkey
Enter fullscreen mode Exit fullscreen mode

Image description

Step 3 Load and read the env variables

In your root folder create an index.js file and then open your terminal and type the below command to install express js.

npm install express --save
Enter fullscreen mode Exit fullscreen mode

This installs expressjs and saves it as a dependency in your package.json file, your package.json looks like this

{
  "name": "env-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.3.1",
    "express": "^4.18.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

and your project structure looks like this

Image description

Now, open your index.js file and type the below command to start a simple web server.

const express = require('express');
const app = express();
const port = 4000;  // defaults to 4000

app.get('/', (req, res) => {
    res.send('Hello World!')
  })

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}/`);
});
Enter fullscreen mode Exit fullscreen mode

run the code with

node index.js
Enter fullscreen mode Exit fullscreen mode

and go to localhost://4000 to get the hello world

Image description

loading and reading the .env file

Now that we have the server running let us read the .env file that we created in the previous step and load the port details from the .env file

Open the index.js file and require the dotenv library there

require('dotenv').config()
Enter fullscreen mode Exit fullscreen mode

now, you can access the .env file using the process.env. Let us use the process.env to access the port number in our index.js file


const express = require('express');
require('dotenv').config()
const app = express();
const port = process.env.PORT || 4000;  // Read from .env if not available then defaults to 4000

app.get('/', (req, res) => {
    res.send('Hello World!')
  })

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}/`);
});
Enter fullscreen mode Exit fullscreen mode

Now let us restart the server and go to localhost://3000 . Instead of running at 4000 our server is now running on port 3000 which it took from the .env file

you can also see this in the console

Image description

You have now successfully created and saved the env file on your machine

As another example you can access the DATABASE_URL like so

const dbUrl = process.env.DATABASE_URL;
// Use dbUrl to connect to your database
Enter fullscreen mode Exit fullscreen mode

Here are all the files

index.js

const express = require('express');
require('dotenv').config()
const app = express();
const port = process.env.PORT || 4000;  // Read from .env if not available then defaults to 4000

app.get('/', (req, res) => {
    res.send('Hello World!')
  })

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}/`);
});
Enter fullscreen mode Exit fullscreen mode

package.json

{
  "name": "env-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.3.1",
    "express": "^4.18.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

.env

DATABASE_URL=sample-database-url
PORT=3000
SECRET_KEY=mysecretkey
Enter fullscreen mode Exit fullscreen mode

How to use env variables in async tasks

In this section we are going to discuss about how env variables can be used in async tasks like calling an API or database operations

Env variables are especially important in these tasks, because they can be used to store credentials and endpoints securely

Also, these variables can be automated because these change between different environment such as development, staging and production.

Step 1 Setting up the env variables

open your .env file and edit it like this

API_URL=https://jsonplaceholder.typicode.com/todos/1
API_KEY=sfjks4325
PORT=3000
Enter fullscreen mode Exit fullscreen mode

Step 2 using async / await with env variables

Next, install the axios library to make calls to the remote server like

npm install axios --save
Enter fullscreen mode Exit fullscreen mode

this will install the axios and save it as a dependency in your package.json file

the package.json file should look something like this

{
  "name": "env-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.5.1",
    "dotenv": "^16.3.1",
    "express": "^4.18.2"
  }
}
Enter fullscreen mode Exit fullscreen mode

Next use the axios to make calls to the jsonplaceholder website and log them to the console

Type this code in your index.js file

const express = require('express');
require('dotenv').config();
const axios = require('axios');
const app = express();
const port = process.env.PORT || 4000;  // Read from .env if not available then defaults to 4000

const fetchData = async () => {
  const url = process.env.API_URL;
  const apiKey = process.env.API_KEY;

  try {
    const response = await axios.get(url, {
      headers: { 'Authorization': `Bearer ${apiKey}` }
    });
    console.log(response.data);
  } catch (error) {
    console.error(`Error fetching data: ${error}`);
  }
};


app.get('/', (req, res) => {
  fetchData()
    res.send('Hello World!')
  })

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}/`);
});
Enter fullscreen mode Exit fullscreen mode

What are we doing here:

  1. We are importing the modules like express, dotenv, axios
  2. Then we are initalizing the app and the port using the env variables
  3. then we are creating the async function called const fetchData = async ()=> (...)
  4. In this function we are using the url and the apiKey from the .env file. Though I have to mention you do not need apiKey to call the jsonplaceholder website. I have just put that key there for demo purposes
  5. we are logging the data to the console
  6. We are calling the fetchData() method on the get route. So, every time someone goes to the / the method gets called and the data gets logged to the console.

Using Async/await with env variables in Database operations

Let us look at another example, this time we are going to do database operations with env variables

step 1 set up the env variables

Open the .env file in your text editor and type the following

DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydatabase
DB_USER=username
DB_PASSWORD=password
Enter fullscreen mode Exit fullscreen mode

Step 2: Install the pg library

next step is to install the pg library like so

npm install pg --save
Enter fullscreen mode Exit fullscreen mode

*Step 3: * Connect to the database and make the async query with .env file

write the following code to connect to the database and use the .env credentials that we just created

const { Pool } = require('pg');
require('dotenv').config();

const pool = new Pool({
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD
});

const fetchUsers = async () => {
  try {
    const res = await pool.query('SELECT * FROM users');
    console.log(res.rows);
  } catch (err) {
    console.error(`Error fetching users: ${err}`);
  }
};

fetchUsers();
Enter fullscreen mode Exit fullscreen mode

we are not integrating this example into our regular codebase because it is an outlier and will not be useful further down the line in our tutorial

Advanced env variable manipulation in Node JS: Encoding, Validation and type conversion

There is more to env variable than just reading and storing them. With advanced env variable manipulation we are going to learn about

  • Encoding
  • Validation and
  • Type conversion

1. Encoding:

Encoding is used for various purposes in environment variables. This could be used for security puposes. The most popular type of encoding is done using the base64

Encoding

const decodedData = Buffer.from(encodedData, 'base64').toString('utf-8');
console.log(`Decoded: ${decodedData}`);
Enter fullscreen mode Exit fullscreen mode

2. Validation

validation is used to check whether the code is valid or not. for example the url that you are going to use is valid url or not etc

or as in this example we are checking whether the port number is within the specified range or not

const validatePort = (port) => {
  const parsedPort = parseInt(port, 10);
  if (isNaN(parsedPort) || parsedPort < 1024 || parsedPort > 65535) {
    throw new Error('Invalid port number');
  }
  return parsedPort;
};

const port = validatePort(process.env.PORT);
Enter fullscreen mode Exit fullscreen mode

3. Type conversion

env variables are always on the type : string. If often you need to use other data types as well such as int or booleans. Here type conversion helps

Integer example

const isProduction = process.env.NODE_ENV === 'production';
const retries = parseInt(process.env.RETRIES, 10) || 3;
Enter fullscreen mode Exit fullscreen mode

Boolean example

const shouldLog = process.env.SHOULD_LOG === 'true';
Enter fullscreen mode Exit fullscreen mode

Combination example

Here is an example combining all the advanced env manipulation available in the env variables

// Encoding and decoding
const apiKey = Buffer.from(process.env.API_KEY || '', 'base64').toString('utf-8');

// Validation
if (apiKey.length !== 32) {
  throw new Error('Invalid API key length');
}

// Type conversion
const maxUsers = parseInt(process.env.MAX_USERS, 10);
if (isNaN(maxUsers)) {
  throw new Error('MAX_USERS must be an integer');
}
Enter fullscreen mode Exit fullscreen mode

Secrets Management and Security Best Practices with examples

In this section we are going to learn about secrets management and best practices with examples. Here are the steps that you can take to secure the env variable along with their examples

1. Never commit the .env files

Always ensure that the .env files are in the gitignore so they are never commited to the git repository

# Ignore dotenv environment variables file
.env
Enter fullscreen mode Exit fullscreen mode

2. Hashing password

always hash the passwords, storing the passwords as plain text is a bad practice. You can use libraries like bcryt and others to hash the passwords

const bcrypt = require('bcrypt');
const saltRounds = 10;

async function hashPassword(plainPassword) {
  const hash = await bcrypt.hash(plainPassword, saltRounds);
  return hash;
}
Enter fullscreen mode Exit fullscreen mode

3. Encoding the env variables

As a security measure always encode the secrets, the secrets are usually base-64 encoded

const buffer = Buffer.from(process.env.MY_SECRET, 'base64');
const decodedSecret = buffer.toString('utf-8');
Enter fullscreen mode Exit fullscreen mode

4. Centralized secrets management

for large projects you can consider centralized secrets management services like hashicorp vault, AWS secrets manager and others

These offer advanced features like secret rotation, automated lease and rotation and audit logging

here is an example with node vault

const vault = require("node-vault")({
  endpoint: "https://vault-endpoint",
  token: process.env.VAULT_TOKEN,
});

async function getDatabaseCreds() {
  const data = await vault.read("path/to/secret");
  return {
    user: data.data.username,
    password: data.data.password,
  };
}
Enter fullscreen mode Exit fullscreen mode

5. Least privilege principle

Always follow the principle of least privilege. The api which does not require write credentials should never be given write privileges.

Common Pitfalls and How to avoid them

  1. Hardcoding Sensitive information
  2. Lack of validation
  3. Ignoring Environments
  4. Inadequate security
  5. Poor Documentation
  6. Fallback defaults

Image description

Need Chat API for your website or app

DeadSimpleChat is an Chat API provider

  • Add Scalable Chat to your app in minutes
  • 10 Million Online Concurrent users
  • 99.999% Uptime
  • Moderation features
  • 1-1 Chat
  • Group Chat
  • Fully Customizable
  • Chat API and SDK
  • Pre-Built Chat

Conclusion

In this article we learned about env variable and how to use them in NodeJs the complete guide

We have covered the important topics related to env management in NodeJs and this should be enough for most of the use-cases for env variables management in Node js apps

I hope you liked the article and thank you for reading

Top comments (1)

Collapse
 
alakkadshaw profile image
alakkadshaw

Thank you for reading.