dotenv & Environment Variables
// .env (NEVER commit):
// PORT=3000
// DATABASE_URL=postgres://user:pass@localhost/db
// JWT_SECRET=supersecretkey123
// NODE_ENV=development
// Load with dotenv: npm install dotenv
require("dotenv").config();
// Or ESM: import "dotenv/config";
// Access:
const PORT = process.env.PORT || 3000;
const isProd = process.env.NODE_ENV === "production";
// Centralize in config.js:
module.exports = {
port: parseInt(process.env.PORT) || 3000,
database: {
url: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === "production"
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: "7d"
}
};
// Validate at startup:
const required = ["DATABASE_URL", "JWT_SECRET"];
const missing = required.filter(k => !process.env[k]);
if (missing.length) {
console.error("Missing env vars:", missing.join(", "));
process.exit(1); // fail fast
}
Best Practices
// .env.example — document variables (DO commit):
// PORT=3000
// DATABASE_URL=postgres://user:password@localhost:5432/dbname
// JWT_SECRET=change-this-to-random-secret
// .gitignore — NEVER commit:
// .env
// .env.local
// .env.production
// Multiple environments:
// .env.development — local dev
// .env.test — test runner
// .env.production — set directly on server, never checked in
// In Docker/CI: set env vars in deployment config
// Never pass secrets through command line (they appear in process list)
// Generating secrets:
// node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
⚡ Key Takeaways
- Never hardcode secrets — use environment variables
- Never commit .env — only commit .env.example with fake values
- Validate all required env vars at startup — fail fast with clear message
- Centralize config in one module — no scattered process.env calls
- Use different .env files per environment
🎯 Practice Exercises
EXERCISE 1
Set up environment variables for your Express API. Validate them at startup. Create a config.js module. Add .env to .gitignore and create .env.example.