I've run into a common issue where my process.env variables ended up being undefined because my configuration files were loaded before dotenv had a chance to initialize. To address this, I set up separate files for development and production environments: .env.development and .env.production. I dynamically load these based on the NODE_ENV value and then import the appropriate configuration.
Here's a snippet of my code:
```js
import dotenv from "dotenv";
const ENV = (process.env.NODE_ENV || "development").trim().toLowerCase();
dotenv.config({ path: `.env.${ENV}` });
let config;
if (ENV === "production") {
const prod = await import("./production.js");
config = prod.default;
} else {
const dev = await import("./development.js");
config = dev.default;
}
export default config;
```
With this setup:
- If NODE_ENV is set to development, it connects to a local MongoDB.
- If NODE_ENV is set to production, it connects to an Atlas cluster.
This approach works well for me, but I'm curious to know how others handle their environment variables and configurations in their projects. Do you guys use a single .env file with conditionals, rely on libraries like config or dotenv-flow, or do you have a different method like using Docker secrets or cloud service configurations? Looking forward to hearing what works for you!
2 Answers
I usually keep a single .env file for development and make sure to ignore it in Git. I have a .env.sample file in the repo that shows the necessary variables without exposing secrets. For production, I rely on environment variables directly instead of using a file.
I think the app should work the same no matter which .env file is used. It’s just about setting the correct flags to load the appropriate file when necessary, but I’d keep it straightforward otherwise.
That makes sense—keeping it simple sounds like a good approach. I've seen some teams overcomplicate it, but it's all about maintaining clarity!