본문으로 건너뛰기 dotenv Complete Guide | Environment Variables in Node.js

dotenv Complete Guide | Environment Variables in Node.js

dotenv Complete Guide | Environment Variables in Node.js

이 글의 핵심

dotenv loads environment variables from a .env file into process.env. It's the standard way to manage configuration and secrets in Node.js applications.

Introduction

dotenv is a zero-dependency module that loads environment variables from a .env file into process.env. It’s the standard for managing configuration in Node.js apps.

The Problem

Hardcoded values (bad):

const db = mysql.createConnection({
  host: 'localhost',
  user: 'admin',
  password: 'secret123', // ❌ Exposed in code!
  database: 'myapp',
});

With dotenv (good):

require('dotenv').config();

const db = mysql.createConnection({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD, // ✅ Secret not in code
  database: process.env.DB_NAME,
});

1. Installation

npm install dotenv

2. Basic Usage

Create .env file:

# .env
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=secret123
DB_NAME=myapp
PORT=3000

Load in your app:

// Load as early as possible
require('dotenv').config();

// Now use environment variables
console.log(process.env.DB_HOST);     // 'localhost'
console.log(process.env.PORT);        // '3000'

3. ES Modules

import 'dotenv/config';

// Or
import dotenv from 'dotenv';
dotenv.config();

console.log(process.env.DB_HOST);

4. Custom Path

require('dotenv').config({ path: '/custom/path/.env' });

// Or multiple files
require('dotenv').config({ path: '.env.local' });
require('dotenv').config({ path: '.env' });

5. Multi-Environment Setup

Development

# .env.development
NODE_ENV=development
DB_HOST=localhost
DB_PORT=5432
API_URL=http://localhost:3000
LOG_LEVEL=debug

Production

# .env.production
NODE_ENV=production
DB_HOST=prod-db.example.com
DB_PORT=5432
API_URL=https://api.example.com
LOG_LEVEL=error

Load Based on Environment

const path = require('path');
const dotenv = require('dotenv');

const envFile = `.env.${process.env.NODE_ENV || 'development'}`;
dotenv.config({ path: path.resolve(process.cwd(), envFile) });

console.log(`Running in ${process.env.NODE_ENV} mode`);

6. Variable Types

String Values

APP_NAME=MyApp
API_KEY=abc123xyz
console.log(process.env.APP_NAME); // 'MyApp'

Numbers

PORT=3000
MAX_CONNECTIONS=100
// Convert to number
const port = parseInt(process.env.PORT, 10);
const maxConn = Number(process.env.MAX_CONNECTIONS);

Boolean Values

DEBUG=true
ENABLE_CACHE=false
const debug = process.env.DEBUG === 'true';
const enableCache = process.env.ENABLE_CACHE === 'true';

JSON Values

# Not recommended, but possible
CONFIG_JSON='{"key":"value","nested":{"prop":true}}'
const config = JSON.parse(process.env.CONFIG_JSON);

7. Default Values

const {
  PORT = 3000,
  DB_HOST = 'localhost',
  NODE_ENV = 'development',
} = process.env;

console.log(PORT); // Uses 3000 if PORT not set

8. Validation

Manual Validation

require('dotenv').config();

const requiredEnvVars = [
  'DB_HOST',
  'DB_USER',
  'DB_PASSWORD',
  'JWT_SECRET',
];

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    throw new Error(`Missing required environment variable: ${envVar}`);
  }
}

With envalid

npm install envalid
require('dotenv').config();
const { str, port, num, bool } = require('envalid');

const env = require('envalid').cleanEnv(process.env, {
  NODE_ENV: str({ choices: ['development', 'test', 'production'] }),
  PORT: port({ default: 3000 }),
  DB_HOST: str(),
  DB_PORT: num({ default: 5432 }),
  ENABLE_HTTPS: bool({ default: false }),
});

console.log(env.PORT); // Validated and converted to number

9. Security Best Practices

1. Never Commit .env

# .gitignore
.env
.env.local
.env.*.local

2. Provide .env.example

# .env.example (commit this!)
DB_HOST=localhost
DB_USER=your_db_user
DB_PASSWORD=your_db_password
JWT_SECRET=your_secret_key
PORT=3000

3. Rotate Secrets Regularly

# Update secrets periodically
JWT_SECRET=new_secret_$(date +%s)

4. Use Strong Secrets

// Generate strong secret
const crypto = require('crypto');
const secret = crypto.randomBytes(64).toString('hex');
console.log(secret);

5. Restrict File Permissions

chmod 600 .env  # Only owner can read/write

10. Production Deployment

Docker

# Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .

# Don't copy .env - use environment variables
CMD ["node", "index.js"]
# docker-compose.yml
version: '3'
services:
  app:
    build: .
    environment:
      DB_HOST: postgres
      DB_USER: admin
      DB_PASSWORD: ${DB_PASSWORD}
    env_file:
      - .env

Heroku

# Set via CLI
heroku config:set DB_HOST=postgres.heroku.com
heroku config:set DB_PASSWORD=secret

# Or via dashboard
# Settings > Config Vars

Vercel

# .vercel.json (don't commit secrets!)
# Or use Vercel dashboard

vercel env add DB_PASSWORD

AWS/GCP

Use secrets managers:

const { SecretsManagerClient } = require('@aws-sdk/client-secrets-manager');

async function getSecret(secretName) {
  const client = new SecretsManagerClient({ region: 'us-east-1' });
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: secretName })
  );
  return JSON.parse(response.SecretString);
}

11. Real-World Example

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

const config = {
  app: {
    name: process.env.APP_NAME || 'MyApp',
    port: parseInt(process.env.PORT, 10) || 3000,
    env: process.env.NODE_ENV || 'development',
  },
  db: {
    host: process.env.DB_HOST,
    port: parseInt(process.env.DB_PORT, 10) || 5432,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    name: process.env.DB_NAME,
  },
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '1h',
  },
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: parseInt(process.env.REDIS_PORT, 10) || 6379,
  },
  email: {
    host: process.env.EMAIL_HOST,
    port: parseInt(process.env.EMAIL_PORT, 10) || 587,
    user: process.env.EMAIL_USER,
    password: process.env.EMAIL_PASSWORD,
  },
};

// Validate critical vars
if (!config.jwt.secret) {
  throw new Error('JWT_SECRET is required');
}

module.exports = config;
// app.js
const config = require('./config');
const express = require('express');

const app = express();

app.listen(config.app.port, () => {
  console.log(`${config.app.name} running on port ${config.app.port}`);
});

12. TypeScript Integration

// env.d.ts
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      NODE_ENV: 'development' | 'production' | 'test';
      PORT: string;
      DB_HOST: string;
      DB_USER: string;
      DB_PASSWORD: string;
      JWT_SECRET: string;
    }
  }
}

export {};
// config.ts
import 'dotenv/config';

interface Config {
  port: number;
  database: {
    host: string;
    user: string;
    password: string;
  };
}

const config: Config = {
  port: parseInt(process.env.PORT, 10),
  database: {
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
  },
};

export default config;

13. Troubleshooting

Variables Not Loading

// Debug mode
require('dotenv').config({ debug: true });

// Check if .env exists
const fs = require('fs');
if (!fs.existsSync('.env')) {
  console.error('.env file not found!');
}

Override Existing Variables

// Force override (not recommended)
require('dotenv').config({ override: true });

Encoding Issues

// Specify encoding
require('dotenv').config({ encoding: 'utf8' });

Summary

dotenv simplifies environment management:

  • Keep secrets out of code
  • Different configs per environment
  • Easy local development
  • Production-ready
  • Zero dependencies

Key Takeaways:

  1. Never commit .env files
  2. Provide .env.example
  3. Validate required variables
  4. Use platform vars in production
  5. Rotate secrets regularly

Next Steps:

Resources:


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Complete dotenv guide for managing environment variables. Learn .env files, secrets management, multi-environment setup,…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers dotenv, Environment Variables, Node.js, Security, Configuration, DevOps.