How to Use Environment Variables from .env in Node.js

May 28, 2024
Written by

How to Use Environment Variables from .env in Node.js

Environment Variables are an exceptionally useful tool available to developers in any server-side environment. And there’s no exception for JavaScript developers using Node.js – they’re used almost everywhere. From serverless code on Twilio Functions or AWS Lambda to building out CI for your projects with GitHub Actions, you’re going to run into Environment Variables at some point.

Let’s dig into the best ways to take advantage of Environment Variables in Node.js, including the recently added built-in tooling that Node.js provides for .env files.

Getting Environment Variables from the Node.js process

In Node.js, the process module exists to provide information about the Node.js process, including environment variables that are provided to it.

For building with both CommonJS and ESM, the process module is always loaded as a global and doesn’t need to be require’ed or import’ed, but it’s generally a good idea to do so.

In CommonJS, you can directly call the process object’s env property:

// app.cjs
const process = require(‘node:process’)
const token = process.env.ID
console.log(token) # logs the value we passed in when we started the app

With ESM, it’s almost the same except for needing to import the process module:

// app.mjs
import process from 'node:process'
const token = process.env.ID
console.log(token) // logs the value we passed in when we started the app

If we wanted to make either of the above examples a bit cleaner, we could use a Named Import to just get the env property that we’re going to use:

// app.cjs
const { env } = require(‘node:process’)
const token = env.ID
console.log(token) # logs the value we passed in when we started the app
// app.mjs
import { env } from 'node:process'
const token = env.ID
console.log(token) // logs the value we passed in when we started the app

If we put any of the above examples into a file called app.cjs or app.mjs, we can run the following command in the terminal and we’ll get the correct log output of the value of ID:

ID=8oz2!Ke@Ac node app.cjs // or .mjs for ESM!
8oz2!Ke@Ac

Getting Environment Variables with the --env-file flag

Storing environment variables in a file called .env has become the go-to way to safely have a functional local development environment with easy-to-manage secrets that can be managed perdeveloper. It’s used so often that it’s part of GitHub’s gitignore for Node.js.

As of Node.js 20.6.0, you can use the experimental --env-file flag to load in one or more .env files (named whatever you’d like!) when you’re running your code:

# .env
ID=8oz2!Ke@Ac

With a .env file like the one above, environment variables in the .env file will be available on the process now, once loaded in with the --env-file flag.

Working with the same app.cjs or app.mjs from before, we can now load up our environment variables from our new .env file, passing it to the --env-file flag:

node app.cjs --env-file=.env

Now, we’ll get the exact same output we had when we were setting the environment variable when running our app, but without risking our tokens being exposed. And, this is all natively available from Node.js itself via the (currently experimental!) --env-file flag, rather than having to take on an additional dependency or devDependency.

Getting Environment Variables with the dotenv module

If you do want to use a well-established module as a dependency that you can independently manage, dotenv is battle tested and works incredibly well.

To add dotenv to our project, we’ll install it:

npm install dotenv

Then, we can enable dotenv in any of the previous code examples by adding one line at the top of the file. In CommonJS, that line is like this:

require('dotenv').config()

And in ESM, it’s this:

import 'dotenv/config'

Once those are in at the top of every single file that needs to have access to the environment variables in your .env file, they should be properly available on the process.env property.

Here’s what the Named Imports CommonJS example from above looks like, tweaked to use dotenv:

// app.cjs
require('dotenv').config() // loads in dotenv and adds values onto process.env
const { env } = require(‘node:process’)
const token = env.ID
console.log(token) # logs the value we passed in when we started the app

And here’s what the Named Imports ESM example from above would look like, again tweaked to use dotenv:

// app.mjs
import 'dotenv/config' // loads in dotenv and adds values onto process.env
import { env } from 'node:process'
const token = env.ID
console.log(token) // logs the value we passed in when we started the app

Conclusion

Hopefully you’re able to take advantage of the --env-var flag in your projects, falling back on the dotenv module where --env-var doesn’t make sense for your use case.

For additional reading on environment variables in Node.js, check out my friend and colleague Dominik Kundel’s post, Working With Environment Variables in Node.js.

If you found this useful, let us know what you’ve built on Twitter at @twiliodevs or on Instagram at @twiliodevs.

Tierney is a Principal Developer Advocate at Twilio. They focus product teams on improving developer experience across the platform and manage Twilio’s public open source. In Tierney’s spare time, they enjoy playing video games with friends, riding an ebike around Prospect Park, and spending time in their favorite cities around the world.