Ditch dotenv: Node.js Now Natively Supports .env Files
Node.js v20.6.0+ natively loads environment variables from `.env` files. Discover how to use the `--env-file` flag or `loadEnvFile()` function to drop the `dotenv` dependency and simplify your setup.
For years, the dotenv package has been the de facto standard for managing environment variables by loading them from .env files in Node.js applications. However, with the release of Node.js v20.6.0, native support for this functionality was introduced, making the dotenv dependency largely redundant.
Node.js now provides built-in methods to load environment variables, either through the --env-file command-line flag or by using the loadEnvFile function from node:process. This article will guide you through transitioning to these native solutions.
The Traditional dotenv Approach (Prior to Node.js v20.6.0)
Before native support, applications typically installed the dotenv package, then imported and called its config() method early in the application's lifecycle.
DATABASE_URL=postgresql://localhost/mydb
import dotenv from 'dotenv';
dotenv.config();
console.log(process.env.DATABASE_URL);
While effective, this approach introduced an external dependency that required ongoing maintenance and updates.
The Native Node.js Solution
Node.js offers two straightforward ways to load .env files without any external packages.
1. Using the CLI Flag
The simplest method is to use the --env-file flag when executing your Node.js application.
node --env-file=.env app.js
You can specify multiple .env files, and values in later files will override those defined in earlier ones. This is useful for managing different environments (e.g., local overrides).
node --env-file=.env --env-file=.env.local app.js
2. Using loadEnvFile
For more programmatic control, you can import and call the loadEnvFile function from node:process.
import { loadEnvFile } from 'node:process';
loadEnvFile(); // Loads .env from the current working directory by default
console.log(process.env.DATABASE_URL);
You can also specify a custom path for your environment file:
import { loadEnvFile } from 'node:process';
loadEnvFile('.env.production'); // Loads a specific .env file
console.log(process.env.DATABASE_URL);
Enhancing with dotenv-defaults
While Node.js's native loadEnvFile handles basic .env loading, you might want to provide sensible default values for your environment variables. This is where the dotenv-defaults package comes in handy. It allows you to create a .env.defaults file with default configurations that are automatically applied unless overridden by a standard .env file.
This approach provides a robust development experience:
- Sensible Defaults: Developers can start working immediately without configuring every variable.
- Version Control Friendly: The
.env.defaultsfile can be committed to version control, establishing a common baseline for all team members. - Local Overrides: Developers only need to create a local
.envfile to override specific variables relevant to their personal setup.
To use dotenv-defaults alongside the native solution, install it:
npm install dotenv-defaults
Then, configure your default variables in a .env.defaults file:
# .env.defaults (Committed to Git - safe defaults for development)
DATABASE_URL=postgresql://localhost:5432/myapp_dev
API_URL=http://localhost:3000
PORT=3000
LOG_LEVEL=info
CACHE_TTL=3600
For local overrides, create a .env file (which should be in your .gitignore):
# .env (In .gitignore - only override what you need)
DATABASE_URL=postgresql://localhost:5432/my_custom_db
API_KEY=my-secret-key
Finally, import dotenv-defaults/config at the entry point of your application. This import automatically loads .env.defaults (if present) and then .env (if present), making the combined environment variables available.
import 'dotenv-defaults/config';
console.log(process.env.DATABASE_URL); // Will reflect the overridden value or default
With this setup, developers can quickly get started with functional defaults, only needing to configure variables that are truly unique to their environment.