Tutorials

Environment Variables in Node.js

February 16, 202612 min readBy Manas

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)

bash
export PORT=3000
export NODE_ENV=production
node app.js

Method 3: .env File (Recommended)

Create a .env file:

text
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

bash
npm 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

javascript
import 'dotenv/config';
// or
import dotenv from 'dotenv';
dotenv.config();

Custom Path

javascript
require('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

javascript
const envFile = process.env.NODE_ENV === 'production' 
  ? '.env.production' 
  : '.env.development';

require('dotenv').config({ path: envFile });

Using dotenv-flow

For automatic environment file loading:

bash
npm install dotenv-flow
javascript
require('dotenv-flow').config();

This automatically loads:

  1. .env
  2. .env.local
  3. .env.[NODE_ENV]
  4. .env.[NODE_ENV].local

Configuration Module Pattern

Create a centralized config:

config.js

javascript
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

javascript
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

javascript
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

bash
npm install envalid
javascript
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:

text
.env
.env.local
.env.*.local

2. Provide Example File

Create .env.example with placeholder values:

text
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

text
# .env
DISCORD_TOKEN=your_bot_token
DISCORD_CLIENT_ID=your_client_id
DISCORD_GUILD_ID=your_guild_id
PREFIX=!
javascript
const { 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
javascript
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

javascript
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

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:

yaml
services:
  app:
    build: .
    environment:
      - NODE_ENV=production
      - PORT=3000
    env_file:
      - .env.production

Troubleshooting

Variables Not Loading

  1. Ensure dotenv is loaded first
  2. Check file path is correct
  3. 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.