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.
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;
Keep sensitive data out of your codebase:
Different values for different environments:
Same code works in different environments without changes.
# Single variable
PORT=3000 node app.js
# Multiple variables
PORT=3000 NODE_ENV=production node app.js
export PORT=3000
export NODE_ENV=production
node app.js
Create a .env file:
PORT=3000
NODE_ENV=development
DATABASE_URL=mongodb://localhost:27017/myapp
API_KEY=your_secret_api_key
dotenv loads variables from .env files into process.env.
npm install dotenv
// 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);
import 'dotenv/config';
// or
import dotenv from 'dotenv';
dotenv.config();
require('dotenv').config({ path: './config/.env' });
.env # Default/shared variables
.env.development # Development-specific
.env.production # Production-specific
.env.test # Test-specific
.env.local # Local overrides (gitignored)
const envFile = process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development';
require('dotenv').config({ path: envFile });
For automatic environment file loading:
npm install dotenv-flow
require('dotenv-flow').config();
This automatically loads:
Create a centralized config:
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'
};
const config = require('./config');
app.listen(config.port, () => {
console.log(\`Server running on port \${config.port}\`);
});
if (config.isDevelopment) {
// Development-only code
}
Validate required variables at startup:
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);
}
}
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
Add to .gitignore:
.env
.env.local
.env.*.local
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
Never use production credentials in development.
Change API keys and tokens periodically.
Only give team members the credentials they need.
# .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);
# .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);
require('dotenv').config();
const mongoose = require('mongoose');
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
Set environment variables in the control panel or use .env files.
// ecosystem.config.js
module.exports = {
apps: [{
name: 'my-app',
script: 'app.js',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production',
PORT: 8080
}
}]
};
# 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
// 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';
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);
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.
VexaNode makes environment variable management easy with built-in support in the control panel for all hosting plans.
Join thousands of satisfied users and experience the VexaNode difference today.
View Plans