🚀 Fixing dotenv in Node.js ES Modules: Why Environment Variables Show undefined and How to Solve It
If you’ve ever worked with Node.js, dotenv, and ES Modules (import syntax), you may have faced this frustrating problem:
👉 Even after adding dotenv.config() at the very top of your code, your .env variables still show up as undefined in your config files.
I ran into this exact issue while setting up my database configuration. After some debugging, I discovered the root cause and found two simple solutions. Let’s break it down step by step.
🔍 The Problem
My project structure looked like this:
project/
├── config/
│ └── db.config.js
├── index.js
└── .env
In my index.js, I had:
import dotenv from "dotenv";
dotenv.config();
import dbConfig from "./config/db.config.js";
And in my db.config.js:
export default {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
};
I expected process.env.DB_HOST and others to load from my .env file.
But instead, I kept getting:
undefined
😤 Super frustrating.
🕵️ Root Cause: Import Hoisting in ES Modules
The reason this happens comes down to how ES Modules work in Node.js.
In ESM,
importstatements are hoisted. This means they run before any other code in the file.So even though I wrote
dotenv.config()at the top ofindex.js, mydb.config.jswas imported before dotenv had a chance to load the.envfile.That’s why my database config was getting
undefined.
👉 In CommonJS (require), this isn’t a problem, because requires happen in order, not hoisted.
✅ Solutions
I found two reliable solutions for this issue, depending on your setup.
Use Node.js
--env-file(Best & Recommended):If you’re on Node.js v20 or later, you can use the built-in
--env-fileflag. This loads your.envfile before anything else runs.Run your app like this:
node --env-file=.env index.jsOr update your
package.json:"scripts": { "start": "node --env-file=.env index.js" }This way,
process.envalready has your environment variables before imports happen.
✅ Clean, ✅ Simple, ✅ Works great for development and testing.Create a Separate dotenv Loader File:
If you don’t want to rely on
--env-fileor you’re using an older Node version, create a small dotenv loader file.For example,
config/env.js:import dotenv from "dotenv"; dotenv.config();Then, in your
index.js:import "./config/env.js"; // Load dotenv first import dbConfig from "./config/db.config.js";This ensures dotenv runs before your database config is evaluated.
It’s a simple and portable fix.
📘 Notes & Learnings
ES Modules execute imports before code, which is why dotenv doesn’t work the way you expect.
Use
--env-filefor a clean solution in Node.js 20+.Alternatively, create a dedicated dotenv loader file if you want portability.
Always check execution order when your environment variables don’t load properly.
🎯 Conclusion
If your .env variables are coming back as undefined in Node.js ES Modules, don’t panic it’s usually because of import hoisting.
✅ The easiest fix:
Use Node’s --env-file flag:
node --env-file=.env index.js
✅ The alternate fix:
Create a dotenv loader file and import it first in your entry file.
Both methods work well for development and testing environments.
1️⃣0️⃣…Crafting code, one bit at a time…0️⃣1️⃣