JS Tower
Node.jsМодуль 6: Безопасность

Переменные окружения

dotenv, конфигурация, секреты

Цель урока

В этом уроке ты научишься:

  • Использовать переменные окружения
  • Настраивать dotenv
  • Управлять конфигурацией

Зачем нужны

  • Хранение секретов (API ключи, пароли)
  • Разные настройки для dev/production
  • Не коммитить чувствительные данные

Никогда не коммить секреты!

Добавь .env в .gitignore.


dotenv

Установка

npm install dotenv

Использование

// В начале приложения
require('dotenv').config();

// Теперь доступны
console.log(process.env.DATABASE_URL);
console.log(process.env.JWT_SECRET);

.env файл

# .env
NODE_ENV=development
PORT=3000

# Database
DATABASE_URL=mongodb://localhost:27017/myapp

# JWT
JWT_SECRET=your-super-secret-key
JWT_EXPIRES_IN=7d

# OAuth
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx

# External APIs
API_KEY=xxx

Разные окружения

Структура файлов

.env              # Общие настройки (не коммитить)
.env.example      # Пример (коммитить)
.env.development  # Для разработки
.env.production   # Для production
.env.test         # Для тестов

.env.example

# .env.example (коммитить в git)
NODE_ENV=development
PORT=3000
DATABASE_URL=
JWT_SECRET=

Загрузка по окружению

const dotenv = require('dotenv');

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

dotenv.config({ path: envFile });

Конфигурационный модуль

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

const config = {
  env: process.env.NODE_ENV || 'development',
  port: parseInt(process.env.PORT, 10) || 3000,
  
  db: {
    url: process.env.DATABASE_URL,
    options: {
      useNewUrlParser: true,
      useUnifiedTopology: true
    }
  },
  
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d'
  },
  
  cors: {
    origin: process.env.CORS_ORIGIN?.split(',') || ['http://localhost:3000']
  },
  
  isProduction: process.env.NODE_ENV === 'production',
  isDevelopment: process.env.NODE_ENV === 'development'
};

// Валидация обязательных переменных
const requiredEnvVars = ['DATABASE_URL', 'JWT_SECRET'];

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

module.exports = config;

Использование

const config = require('./config');

mongoose.connect(config.db.url, config.db.options);

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

Валидация с Joi

const Joi = require('joi');

const envSchema = Joi.object({
  NODE_ENV: Joi.string()
    .valid('development', 'production', 'test')
    .default('development'),
  PORT: Joi.number().default(3000),
  DATABASE_URL: Joi.string().required(),
  JWT_SECRET: Joi.string().min(32).required(),
  JWT_EXPIRES_IN: Joi.string().default('7d')
}).unknown();

const { error, value } = envSchema.validate(process.env);

if (error) {
  throw new Error(`Config validation error: ${error.message}`);
}

module.exports = {
  env: value.NODE_ENV,
  port: value.PORT,
  db: { url: value.DATABASE_URL },
  jwt: { secret: value.JWT_SECRET, expiresIn: value.JWT_EXPIRES_IN }
};

Секреты в production

Не используй .env в production

Вместо этого:

  • Переменные окружения сервера
  • Secret managers (AWS Secrets Manager, HashiCorp Vault)
  • Kubernetes Secrets
  • Docker secrets

Пример Docker

# Dockerfile
ENV NODE_ENV=production
# Не копируй .env в образ!
# docker-compose.yml
services:
  app:
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - JWT_SECRET=${JWT_SECRET}

Практика

Задание: Конфигурация

Задача: Создай модуль конфигурации с валидацией.

Решение:

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

const required = ['DATABASE_URL', 'JWT_SECRET'];

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

module.exports = {
  port: process.env.PORT || 3000,
  db: process.env.DATABASE_URL,
  jwt: {
    secret: process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d'
  },
  isProd: process.env.NODE_ENV === 'production'
};

Проверь себя

  1. Почему нельзя коммитить .env?
  2. Как загрузить разные .env для разных окружений?
  3. Как хранить секреты в production?