Environment Variables in Node.js
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.
javascript// 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
bash# Single variable PORT=3000 node app.js # Multiple variables PORT=3000 NODE_ENV=production node app.js
Method 2: Export (Unix/Mac)
bashexport PORT=3000 export NODE_ENV=production node app.js
Method 3: .env File (Recommended)
Create a .env file:
textPORT=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
bashnpm install dotenv
Basic Usage
javascript// 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
javascriptimport 'dotenv/config'; // or import dotenv from 'dotenv'; dotenv.config();
Custom Path
javascriptrequire('dotenv').config({ path: './config/.env' });
Environment-Specific Files
Multiple .env Files
text.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
javascriptconst envFile = process.env.NODE_ENV === 'production' ? '.env.production' : '.env.development'; require('dotenv').config({ path: envFile });
Using dotenv-flow
For automatic environment file loading:
bashnpm install dotenv-flow
javascriptrequire('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
javascriptrequire('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
javascriptconst 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
javascriptconst 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
bashnpm install envalid
javascriptconst { 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:
text.env .env.local .env.*.local
2. Provide Example File
Create .env.example with placeholder values:
textPORT=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
text# .env DISCORD_TOKEN=your_bot_token DISCORD_CLIENT_ID=your_client_id DISCORD_GUILD_ID=your_guild_id PREFIX=!
javascriptconst { Client, GatewayIntentBits } = require('discord.js'); require('dotenv').config(); const client = new Client({ intents: [GatewayIntentBits.Guilds] }); client.login(process.env.DISCORD_TOKEN);
Express API
text# .env PORT=3000 NODE_ENV=development DATABASE_URL=mongodb://localhost:27017/api JWT_SECRET=super_secret_key CORS_ORIGIN=http://localhost:3001
javascriptrequire('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
javascriptrequire('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
javascript// 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# Dockerfile ENV NODE_ENV=production ENV PORT=3000
Or with docker-compose:
yamlservices: 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
javascript// 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:
javascript// 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.
Deploy on HeavenCloud
Discord bots, Lavalink nodes, and VPS, instant setup with NVMe and DDoS protection.