Lesson 18 of 20

Environment Variables & Config

Using dotenv and process.env

Environment variables store configuration that changes between environments (development, staging, production) — like database URLs, API keys, and secret tokens. They keep sensitive data out of your source code.

The dotenv package loads variables from a .env file into process.env. This is the standard approach in Node.js projects. The .env file should never be committed to version control.

Example
// npm install dotenv

// .env file (in project root)
// PORT=3000
// NODE_ENV=development
// DATABASE_URL=mongodb://localhost:27017/myapp
// JWT_SECRET=my-super-secret-key-change-this
// API_KEY=abc123def456

// Load environment variables (at the top of your entry file)
require('dotenv').config();

// Access variables
const PORT = process.env.PORT || 3000;
const DB_URL = process.env.DATABASE_URL;
const JWT_SECRET = process.env.JWT_SECRET;

console.log('Environment:', process.env.NODE_ENV);
console.log('Port:', PORT);

// Use in your app
const mongoose = require('mongoose');
mongoose.connect(DB_URL);

const app = require('express')();
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
  • process.env — Object containing all environment variables
  • dotenv — Loads .env file into process.env
  • .env — File containing KEY=VALUE pairs (one per line)
  • .env.example — Template file showing required variables (commit this)
  • Never commit .env — Add it to .gitignore immediately
Notes
  • Create a .env.example file with empty values as a template for other developers. Commit .env.example but never .env.

Config Management Pattern

For larger applications, create a config module that validates and exports all configuration in one place. This makes it easy to see what your app needs and catches missing variables at startup.

This pattern provides defaults for development and ensures required variables are set in production.

Example
// config/index.js
require('dotenv').config();

function requireEnv(name) {
  const value = process.env[name];
  if (!value) {
    throw new Error(`Missing required environment variable: ${name}`);
  }
  return value;
}

const config = {
  port: parseInt(process.env.PORT, 10) || 3000,
  nodeEnv: process.env.NODE_ENV || 'development',
  isDev: process.env.NODE_ENV !== 'production',

  db: {
    url: requireEnv('DATABASE_URL'),
  },

  jwt: {
    secret: requireEnv('JWT_SECRET'),
    expiresIn: process.env.JWT_EXPIRES_IN || '24h',
  },

  cors: {
    origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
  },
};

module.exports = config;

// Usage in other files
const config = require('./config');

mongoose.connect(config.db.url);
app.listen(config.port);

const token = jwt.sign(payload, config.jwt.secret, {
  expiresIn: config.jwt.expiresIn,
});
Notes
  • Call requireEnv() for variables that must be set (database URL, secrets). Use defaults for variables that can have fallback values (port, environment).