Express.jsMiddleTechnical

Как управлять конфигурацией, специфичной для разных окружений, в Express?

Конфигурацию окружений в Express управляют через переменные окружения (process.env), библиотеки dotenv/.env-файлы и пакеты вроде convict или envalid для валидации схемы. NODE_ENV управляет выбором нужного конфига.

Управление конфигурацией для разных окружений в Express

Стандартный подход строится на переменных окружения и файлах .env. Библиотека dotenv загружает переменные из файла в process.env, а NODE_ENV определяет, какой набор значений активен.

Базовая структура

npm install dotenv dotenv-expand

Файловая структура проекта:

.env              # базовые дефолты (не коммитить секреты)
.env.development  # локальная разработка
.env.test         # CI/тесты
.env.production   # продакшн (только неконфиденциальные переопределения)
.env.local        # локальные override, в .gitignore

Загрузка конфига

// src/config/env.js
import 'dotenv/config';
import { expand } from 'dotenv-expand';
import * as dotenv from 'dotenv';

const env = dotenv.config({
  path: `.env.${process.env.NODE_ENV ?? 'development'}`,
  override: false,
});
expand(env);

Валидация схемы через envalid

Без валидации приложение может стартовать с неправильными переменными и упасть в runtime. envalid падает при старте, если что-то не так:

// src/config/index.js
import { cleanEnv, str, port, url, bool } from 'envalid';

export const config = cleanEnv(process.env, {
  NODE_ENV: str({ choices: ['development', 'test', 'production'] }),
  PORT: port({ default: 3000 }),
  DATABASE_URL: url(),
  JWT_SECRET: str({ docs: 'https://wiki.internal/jwt' }),
  REDIS_URL: url({ default: 'redis://localhost:6379' }),
  ENABLE_FEATURE_X: bool({ default: false }),
});
// src/app.js
import express from 'express';
import { config } from './config/index.js';

const app = express();

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

Разделение поведения по окружениям

// src/app.js
if (config.NODE_ENV === 'development') {
  const morgan = await import('morgan');
  app.use(morgan.default('dev'));
}

if (config.NODE_ENV === 'production') {
  const compression = await import('compression');
  app.use(compression.default());
}

Секреты в продакшне

На продакшне секреты хранят в менеджерах вроде AWS Secrets Manager, HashiCorp Vault или Doppler — и не в .env-файлах. Типовой паттерн: при старте контейнера секреты инжектятся как переменные окружения через entrypoint-скрипт или оркестратор (Kubernetes Secrets, ECS Task Definition).

Подводные камни

  • Коммит .env.production с секретами в репозиторий — утечка credentials.
  • Отсутствие валидации схемы: приложение стартует с undefined и падает спустя время с непонятной ошибкой.
  • Вызов dotenv.config() после импорта модулей, которые уже обращались к process.env — переменные не загружены вовремя.
  • Использование разных имён переменных в dev и prod — сложно отлаживать рассинхронизацию.
  • NODE_ENV не установлен явно в продакшне: Express по умолчанию отключает ряд оптимизаций (кеш шаблонов, сокращённые stack traces).
  • Хранение конфига как глобального singleton без unit-тестирования — тяжело мокировать в тестах.
  • Отсутствие .env.example в репозитории: новые разработчики не знают, какие переменные нужны.

Common mistakes

  • Дает общий ответ про Node.js и не называет конкретные API Express.js.
  • Не объясняет, где в lifecycle находится конфигурация по окружениям.
  • Не разделяет validation, authorization, business logic и persistence.
  • Игнорирует ошибки, лимиты входных данных, observability и тестирование.

What the interviewer is testing

  • Может объяснить конфигурация по окружениям на примере кода.
  • Называет ключевые API: process.env, config schema.
  • Использует точные API Express.js, а не вымышленные hooks/decorators/methods.
  • Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.

Sources

Related topics