Environment Variables in Node.js
Managing configuration across development and production. Here's how I handle env vars.
Why Environment Variables
Config that changes between environments:
- Database URLs
- API keys
- Feature flags
- Debug settings
These should NOT be hardcoded or committed to git.
The Basics
Node.js reads from `process.env`:
const dbUrl = process.env.DATABASE_URL;
const port = process.env.PORT || 3000;
Set them when running:
DATABASE_URL=postgres://localhost/mydb node server.js
Using dotenv
For local development, use `.env` files:
npm install dotenv
# .env
DATABASE_URL=postgres://localhost/mydb
API_KEY=secret123
NODE_ENV=development
// Load at app start
require('dotenv').config();
// Or with ES modules
import 'dotenv/config';
// Now available
console.log(process.env.DATABASE_URL);
Multiple Environments
.env # Default/development
.env.local # Local overrides (gitignored)
.env.production # Production settings
.env.test # Test settings
Load the right one:
require('dotenv').config({
path: `.env.${process.env.NODE_ENV || 'development'}`
});
Validation
Fail fast if required vars are missing:
const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];
required.forEach(key => {
if (!process.env[key]) {
throw new Error(`Missing required env var: ${key}`);
}
});
Or use a library like `envalid`:
const { cleanEnv, str, port } = require('envalid');
const env = cleanEnv(process.env, {
DATABASE_URL: str(),
PORT: port({ default: 3000 }),
NODE_ENV: str({ choices: ['development', 'production', 'test'] })
});
Don't Commit Secrets
# .gitignore
.env
.env.local
.env.*.local
Commit `.env.example` instead:
# .env.example
DATABASE_URL=postgres://user:pass@localhost/db
API_KEY=your-api-key-here
Production
In production, set env vars through:
- Docker Compose environment
- Kubernetes secrets
- Platform settings (Vercel, Railway, etc.)
- CI/CD pipeline variables
Never put production secrets in files committed to git.