On this page
Environment Variables
Environment variables store configuration outside your code — database URLs, API keys, port numbers — keeping secrets out of source control.
Reading Environment Variables
const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || 'development';
console.log(`Running on port ${port} in ${nodeEnv} mode`);
Setting Variables
# Linux/macOS
PORT=8080 NODE_ENV=production node app.js
# Windows (cmd)
set PORT=8080 && node app.js
# Windows (PowerShell)
$env:PORT=8080; node app.js
Using dotenv
Install:
npm install dotenv
Create .env (never commit to git):
PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your-secret-key-here
API_KEY=abc123
Load in your app:
import 'dotenv/config'; // ES modules — loads at import
// Or
import dotenv from 'dotenv';
dotenv.config();
const port = process.env.PORT;
const dbUrl = process.env.DATABASE_URL;
.env.example
Commit a template without secrets:
PORT=3000
NODE_ENV=development
DATABASE_URL=
JWT_SECRET=
Developers copy to .env and fill in values.
.gitignore
.env
.env.local
.env.*.local
Validating Environment Variables
Fail fast if required variables are missing:
const required = ['PORT', 'DATABASE_URL', 'JWT_SECRET'];
for (const key of required) {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
Or use zod / envalid for schema validation:
import { cleanEnv, str, num } from 'envalid';
const env = cleanEnv(process.env, {
PORT: num({ default: 3000 }),
DATABASE_URL: str(),
JWT_SECRET: str()
});
NODE_ENV Values
| Value | Purpose |
|---|---|
development |
Local dev, verbose logging |
production |
Optimized, minimal logging |
test |
Running tests |
Many libraries change behavior based on NODE_ENV.
Best Practices
- Never hardcode secrets in source code
- Use different
.envfiles per environment - Rotate secrets regularly
- Use secret managers (AWS Secrets Manager, Vault) in production