Environment Variables in Node.js: .env Files, dotenv, and Best Practices
Everything about environment variables in Node.js — .env files, the dotenv package, security best practices, and how to manage configs across development, staging, and production.
What Are Environment Variables?
Environment variables are key-value pairs that configure your application without hardcoding values in your source code. They're used for:
- Secrets — API keys, database passwords, JWT secrets
- Configuration — Port numbers, API URLs, feature flags
- Environment-specific settings — Different values for dev, staging, production
# Setting environment variables
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=sk_live_abc123
PORT=3000
NODE_ENV=production
The .env File
The .env file stores environment variables in your project root:
# .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY=sk_live_abc123
JWT_SECRET=my-super-secret-key
PORT=3000
NODE_ENV=development
# Comments start with #
# Quotes are optional but recommended for values with spaces
APP_NAME="My Cool App"
.env File Rules
- One variable per line —
KEY=value - No spaces around
=—KEY=valuenotKEY = value - Comments with
#—# This is a comment - Quotes for special characters —
VALUE="hello world" - No trailing commas or semicolons
Validate your .env files with our .env Validator.
Using dotenv in Node.js
npm install dotenv
// Load at the very top of your entry file
require("dotenv").config();
// Or with ES modules
import "dotenv/config";
// Access variables
const dbUrl = process.env.DATABASE_URL;
const port = process.env.PORT || 3000;
const isProduction = process.env.NODE_ENV === "production";
With TypeScript
// Create a typed config
interface Config {
DATABASE_URL: string;
API_KEY: string;
PORT: number;
NODE_ENV: "development" | "production" | "test";
}
function getConfig(): Config {
const required = ["DATABASE_URL", "API_KEY"];
for (const key of required) {
if (!process.env[key]) {
throw new Error(\`Missing required environment variable: \${key}\`);
}
}
return {
DATABASE_URL: process.env.DATABASE_URL!,
API_KEY: process.env.API_KEY!,
PORT: parseInt(process.env.PORT || "3000", 10),
NODE_ENV: (process.env.NODE_ENV as Config["NODE_ENV"]) || "development",
};
}
export const config = getConfig();
Multiple Environment Files
.env # Default (loaded always)
.env.local # Local overrides (not committed)
.env.development # Development-specific
.env.production # Production-specific
.env.test # Test-specific
Loading Order (Next.js)
.env.local(highest priority).env.[NODE_ENV](e.g.,.env.production).env(lowest priority)
.env.example
Always include a .env.example file that documents all required variables WITHOUT actual values:
# .env.example — Copy to .env and fill in values
DATABASE_URL=
API_KEY=
JWT_SECRET=
PORT=3000
NODE_ENV=development
Security Best Practices
1. NEVER Commit .env Files
Add to .gitignore:
.env
.env.local
.env.*.local
Use our .gitignore Generator to create a proper .gitignore.
2. Use Different Secrets Per Environment
# Development
JWT_SECRET=dev-secret-not-important
# Production
JWT_SECRET=a8f2k9d7e3b1c5h6g4j0m8n2p7q9r1s3t5u7v0w2x4y6z8
Generate strong secrets with our Password Generator or Random String Generator.
3. Validate on Startup
const required = ["DATABASE_URL", "API_KEY", "JWT_SECRET"];
const missing = required.filter((key) => !process.env[key]);
if (missing.length > 0) {
console.error(\`Missing environment variables: \${missing.join(", ")}\`);
process.exit(1);
}
4. Never Expose Server Variables to the Client
In Next.js, only variables prefixed with NEXT_PUBLIC_ are exposed to the browser:
# Server-only (safe)
DATABASE_URL=postgres://...
API_SECRET=sk_live_...
# Exposed to browser (be careful!)
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_SITE_NAME=My App
Related Tools
- .env Validator — Validate .env file syntax
- .gitignore Generator — Protect .env files
- Password Generator — Generate secrets
- Random String Generator — Generate API keys
- Base64 Encoder — Encode sensitive values
- JSON Formatter — Format config files