Environment variables are essential for configuring Node.js applications. They keep sensitive data out of your code and allow different configurations for development, staging, and production.
What Are Environment Variables?
Environment variables are key-value pairs available to your application at runtime. They are set outside your code, making it easy to change configuration without modifying source files.
// Accessing environment variables
const port = process.env.PORT;
const dbUrl = process.env.DATABASE_URL;
Why Use Environment Variables?
Security
Keep sensitive data out of your codebase:
- API keys
- Database credentials
- Secret tokens
- Third-party service credentials
Flexibility
Different values for different environments:
- Development database vs production database
- Debug mode in development
- Different API endpoints
Portability
Same code works in different environments without changes.
Setting Environment Variables
Method 1: Command Line
# Single variable
PORT=3000 node app.js
# Multiple variables
PORT=3000 NODE_ENV=production node app.js
Method 2: Export (Unix/Mac)
export PORT=3000
export NODE_ENV=production
node app.js
Method 3: .env File (Recommended)
Create a .env file:
PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/myapp
API_KEY=your_secret_api_key
Using dotenv
dotenv loads variables from .env files into process.env.
Installation
npm install dotenv
Basic Usage
// At the very top of your entry file
require('dotenv').config();
// Now you can use process.env
console.log(process.env.PORT);
console.log(process.env.DATABASE_URL);
ES Modules
import 'dotenv/config';
// or
import dotenv from 'dotenv';
dotenv.config();
Custom Path
require('dotenv').config({ path: './config/.env' });
Environment-Specific Files
Multiple .env Files
.env # Default/shared variables
.env.development # Development-specific
.env.production # Production-specific
.env.test # Test-specific
.env.local # Local overrides (gitignored)
Loading Based on Environment
const envFile = process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development';
require('dotenv').config({ path: envFile });
Using dotenv-flow
For automatic environment file loading:
npm install dotenv-flow
require('dotenv-flow').config();
This automatically loads:
- .env
- .env.local
- .env.[NODE_ENV]
- .env.[NODE_ENV].local
Configuration Module Pattern
Create a centralized config:
config.js
require('dotenv').config();
module.exports = {
port: process.env.PORT || 3000,
nodeEnv: process.env.NODE_ENV || 'development',
database: {
url: process.env.DATABASE_URL,
name: process.env.DB_NAME || 'myapp'
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
},
discord: {
token: process.env.DISCORD_TOKEN,
clientId: process.env.DISCORD_CLIENT_ID
},
isDevelopment: process.env.NODE_ENV === 'development',
isProduction: process.env.NODE_ENV === 'production'
};
Usage
const config = require('./config');
app.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});
if (config.isDevelopment) {
// Development-only code
}
Validation
Validate required variables at startup:
Simple Validation
const required = ['DATABASE_URL', 'JWT_SECRET', 'DISCORD_TOKEN'];
for (const key of required) {
if (!process.env[key]) {
console.error(`Missing required environment variable: ${key}`);
process.exit(1);
}
}
Using envalid
npm install envalid
const { cleanEnv, str, port, url } = require('envalid');
const env = cleanEnv(process.env, {
NODE_ENV: str({ choices: ['development', 'production', 'test'] }),
PORT: port({ default: 3000 }),
DATABASE_URL: url(),
JWT_SECRET: str(),
DISCORD_TOKEN: str()
});
// env is now validated and typed
console.log(env.PORT); // number
console.log(env.isDev); // boolean helper
Security Best Practices
1. Never Commit .env Files
Add to .gitignore:
.env
.env.local
.env.*.local
2. Provide Example File
Create .env.example with placeholder values:
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp
JWT_SECRET=your_jwt_secret_here
DISCORD_TOKEN=your_discord_token_here
3. Use Different Values Per Environment
Never use production credentials in development.
4. Rotate Secrets Regularly
Change API keys and tokens periodically.
5. Limit Access
Only give team members the credentials they need.
Common Patterns
Discord Bot
# .env
DISCORD_TOKEN=your_bot_token
DISCORD_CLIENT_ID=your_client_id
DISCORD_GUILD_ID=your_guild_id
PREFIX=!
const { Client, GatewayIntentBits } = require('discord.js');
require('dotenv').config();
const client = new Client({
intents: [GatewayIntentBits.Guilds]
});
client.login(process.env.DISCORD_TOKEN);
Express API
# .env
PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/api
JWT_SECRET=super_secret_key
CORS_ORIGIN=http://localhost:3001
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: process.env.CORS_ORIGIN
}));
app.listen(process.env.PORT);
Database Connection
require('dotenv').config();
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
Hosting Provider Configuration
HeavenCloud
Set environment variables in the control panel or use .env files.
PM2
// ecosystem.config.js
module.exports = {
apps: [{
name: 'my-app',
script: 'app.js',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production',
PORT: 8080
}
}]
};
Docker
# Dockerfile
ENV NODE_ENV=production
ENV PORT=3000
Or with docker-compose:
services:
app:
build: .
environment:
- NODE_ENV=production
- PORT=3000
env_file:
- .env.production
Troubleshooting
Variables Not Loading
- Ensure dotenv is loaded first
- Check file path is correct
- Verify .env file syntax (no spaces around =)
Undefined Values
// Check if variable exists
if (!process.env.MY_VAR) {
console.log('MY_VAR is not set');
}
// Provide defaults
const myVar = process.env.MY_VAR || 'default_value';
Type Issues
Environment variables are always strings:
// Wrong
if (process.env.ENABLE_FEATURE) // Always truthy if set
// Correct
if (process.env.ENABLE_FEATURE === 'true')
// Or parse
const port = parseInt(process.env.PORT, 10);
Conclusion
Environment variables are fundamental to Node.js application configuration. Use dotenv for local development, validate required variables at startup, and never commit sensitive data to version control.
HeavenCloud makes environment variable management easy with built-in support in the control panel for all hosting plans.