Как управлять конфигурацией, специфичной для разных окружений, в 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-риски: безопасность, отказоустойчивость, логирование и тесты.