Back

Handling env files in the latest Node.JS

Handling env files in the latest Node.JS

The Node.JS team released several versions recently; among these is the v20.6.0, which comes with built-in support for env files. In this article, you’ll learn about env files, how they were handled before, and how you can now handle env files in the latest releases.

Environment files (“env files”) safely store environment variables, which serve a purpose in your code but have to be kept separate from your code.

These variables could be API keys, database configurations or some other secret that should be hidden from the user.

These variables are stored in key-value pairs. The code below is an example of a key-value pair.

MY_SECRET_KEY=my-secret-value

In this example, MY_SECRET_KEY is the key while my-secret-value represents the value.

To use an environment variable, the key is specified in the code while the value is safely hidden in an env file. The value is then accessed at runtime using the process.env variable.

Runtime: Runtime refers to the period within which your code executes in an environment, from when it starts running to when it stops. The runtime environment for Javascript is Node.JS.

Env files are important because they keep env variables and other private information from falling into the wrong hands while providing easy access when the variables are needed.

Interestingly, Node.JS doesn’t have built-in support for env files in Node.JS versions earlier than v20.6.0. This means projects using those Node versions would need to handle env files using other means.

How env files were handled with Node.JS

Before the recent Node.JS releases, env files were handled using third-party packages. A prevalent package to handle env files is the dotenv package - a npm package that loads environment variables from env files in your project. As of writing, the dotenv package has over 35m weekly downloads on npm - showing its popularity and reliability regarding handling env files.

To load env files in your Node.JS project using the dotenv package, you would need to do the following things:

  1. Install the dotenv package using
npm install dotenv

or if you use yarn

yarn install dotenv
  1. Create a .env file and add your variable(s)
YOUR_SECRET_KEY=your-secret-value
  1. Import the package at the top level of your Node.JS projects and call its config() function
require("dotenv").config();
  1. Load the variable into your code using
process.env.YOUR_KEY

Now, Node v20.6.0 and above will load env variables from env files without needing a different package.

Node.JS new handling of env files

Handling env files in the new Node.JS releases is similar to the dotenv package, except you don’t need to import any package. You just need to do the following:

  1. Add a .env file to your folder and define your key-value pair(s).
  2. Use process.env to access the variable in your env file.
  3. Start or restart your Node.JS application using the node command and the --env-file flag.

A necessary prerequisite for these actions is to have the appropriate version of Node.JS installed on your device, and you can check the Node.JS version you have by typing node -v in your terminal.

If you have a Node version earlier than v20.6.0, you must upgrade it.

You can upgrade your Node version using either the nvm package or by downloading and installing a new Node.JS version from the Node.JS website.

Using env files in a Node.JS app

In this section, we’ll connect to a Mongodb database from a Node.JS application and use an env file to store our database connection string.

Setting up a Node.JS server

First, we need a Node.JS server:

  1. Start a new Node.JS application using
npm init -y

This will create a default package.json file in your directory.

  1. Create an index.js file in your root folder; your code will be in this file. Your folder structure should look like the one below
|-- index.js
|-- package.json

Now add this code to your index.js file

//index.js
const http = require("node:http");
const hostname = "127.0.0.1";
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello world");
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

The code above creates a basic Node.JS server using the http protocol. It uses the http protocol to create a server and serve a “Hello world” response on port 3000.

Setting up the Mongo database

Install MongoDB using,

npm install mongodb

then, at the top level of the index.js file import MongoClient and ServerApiVersion.

const { MongoClient, ServerApiVersion } = require('mongodb');

Next, add the Mongo boilerplate for connecting to your database

const client = new MongoClient(uri, {
  serverApi: {
    version: ServerApiVersion.v1,
    strict: true,
    deprecationErrors: true,
  },
});

async function run() {
  try {
    // Connect the client to the server (optional starting in v4.7)
    await client.connect();
    // Send a ping to confirm a successful connection
    await client.db("admin").command({ ping: 1 });
    console.log(
      "Pinged your deployment. You successfully connected to MongoDB!",
    );
  } finally {
    // Ensures that the client will close when you finish/error
    await client.close();
  }
}
run().catch(console.dir);

Using environment variables

To connect to MongoDB you will need a connection string. You can get this string when you create an account and a database for your project.

Below is an example of a connection string.

mongodb+srv://<YOUR_NAME>:<YOUR_PROJECT_NAME>.mongodb.net/?retryWrites=true&w=majority

This string grants read/write access to your database so it’s something you can not expose in your code.

You can store this connection string by doing the following,

  1. Create a .env file at the root of your folder

  2. In the .env file, add your secret; the secret for this example will be the MongoDB connection string.

MONGO_DB_URI = YOUR_MONGO_STRING_HERE
  1. Create a .gitignore file in the root of your project, giving you a folder structure like the one below
|-- node_modules
|-- index.js
|-- package-lock.json
|-- package.json
|-- .env
|-- .gitignore

A gitignore file keeps the specified file from getting pushed to Github. Inside the .gitignore file, you just need to type .env, and your .env file will be kept from Github.

  1. Now, go to the index.js file and add this just below the imports
const uri = `${process.env.MONGO_DB_URI}`;

Your connection string has now been assigned to the variable URI, which gives MongoClient access to your database. You don’t need to do anything else since uri has already been used in MongoClient in the boilerplate

const client = new MongoClient(uri, {
  //rest of the code here
});

Starting the server

Now here’s something interesting, if you start the server using node index.js you will get an error in your console

MongoParseError: Invalid scheme, expected connection string to start with "mongodb://" 
or "mongodb+srv://"

This error is because you haven’t used the -env-file flag to tell Node to watch the .env file where you have the connection string.

So, to properly restart the server you need to use,

node --env-file=.env index.js

Now, Node knows to watch the .env file for environment variables, and process.env makes the env variable available at runtime.

If you check your console, you should see,

Server running at http://127.0.0.1:3000/

After a second or two you should also see a database connection success message.

Pinged your deployment. You successfully connected to MongoDB!

Now your server is securely connected to your database without needing a third-party package like dotenv.

Loading multiple env files

There may be times when you’ll have different env files for a production environment and a local environment. This means you’ll have two env files in a project, a .env file, and a .env.local file.

Node allows watching multiple env files, so if you want to watch two env files like .env.local and .env together, you just need to do it like so,

node --env-file=.env.local --env-file=.env index.js

Note that if you have environment variables with the same key in both env files, the variable in the latter env file will override the former. Here’s a basic example:

.env.local
SECRET_KEY=a-secret-key
.env
SECRET_KEY=another-secret-key

The two env files have the same key, SECRET_KEY; if you load them together using the command above, Node.JS will not treat them as separate env variables. Instead, the env variable for .env will override the variable for .env.local.

If you change the order and type it like,

node --env-file=.env --env-file=.env.local index.js

The variable in .env.local will override that of the .env file.

While this can be frustrating if you type the flags in an incorrect order, it could also be helpful if you cannot or do not want to change the secrets in a certain env file, and you need to load other variables in that file. This way you can override a single variable without changing the file.

Conclusion

This article has talked about what env files are, how env files used to be handled in Node.JS using the dotenv package, and how the Node.JS new releases now support env files.

You can check the Node.JS release docs to see other features that came out in the recent releases and how they can help you build better Node.JS applications.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay