Переменные окружения и конфигурация
Секреты и настройки живут в переменных окружения, а не в коде: .env для разработки, реальное окружение в проде.
«Конфиг отделяют от кода: один и тот же образ приложения работает и локально, и на проде — меняются только переменные.»
Строка подключения к базе, секрет для JWT, ключи внешних сервисов — всё это секреты, которым не место в коде и тем более в git. Стандартное решение — переменные окружения. В этом уроке разберём, как читать их в Express, как использовать файл .env в разработке и зачем нужен NODE_ENV.
process.env и .env
Node даёт доступ к переменным окружения через process.env. В разработке их удобно держать в файле .env и загружать пакетом dotenv (или встроенным флагом Node):
# .env (НЕ коммитим в git!)
PORT=3000
DATABASE_URL=postgres://localhost/myapp
JWT_SECRET=очень-длинный-случайный-секретrequire('dotenv').config(); // загрузит .env в process.env
const PORT = process.env.PORT || 3000;
const SECRET = process.env.JWT_SECRET;
if (!SECRET) {
throw new Error('JWT_SECRET не задан -- приложение не запустится');
}
app.listen(PORT);Проверка наличия обязательных переменных на старте — хорошая привычка: лучше упасть сразу с понятной ошибкой, чем загадочно сломаться позже.
NODE_ENV и режимы
Переменная NODE_ENV сообщает приложению, в каком режиме оно работает. В продакшене её ставят в production — Express при этом включает оптимизации и ускоряется в несколько раз:
const isProd = process.env.NODE_ENV === 'production';
// в проде -- лаконичные ошибки, в разработке -- подробные
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
error: err.message,
stack: isProd ? undefined : err.stack // стек только в dev
});
});Загрузчик конфига в браузере
Смоделируем чтение конфигурации с дефолтами и проверкой обязательных полей — типовой паттерн старта Express-приложения:
function loadConfig(env) {
const required = ['JWT_SECRET', 'DATABASE_URL'];
for (const key of required) {
if (!env[key]) throw new Error('Не задана переменная: ' + key);
}
return {
port: Number(env.PORT) || 3000,
secret: env.JWT_SECRET,
db: env.DATABASE_URL,
isProd: env.NODE_ENV === 'production'
};
}
const config = loadConfig({
PORT: '8080',
JWT_SECRET: 'abc',
DATABASE_URL: 'postgres://...'
});
console.log(config.port); // 8080
console.log(config.isProd); // falseКак работает под капотом
dotenv читает файл .env построчно и дописывает пары ключ-значение в process.env ещё до запуска остального кода — поэтому require('dotenv').config() ставят первой строкой. В продакшене файла .env часто нет вовсе: переменные задаёт система запуска (Docker, PM2, облако), а приложение читает их из того же process.env, ничего не зная об источнике.
Частые ошибки
- Закоммитить .env. Это утечка всех секретов. Добавь его в
.gitignoreсразу. - Хардкодить секреты в коде. Их нельзя ротировать без правки исходников и они попадают в историю git.
- Забыть NODE_ENV=production. Без него Express работает медленнее и выдаёт лишние детали ошибок.
Best practices
- Все секреты и настройки — только в переменных окружения.
- Держи
.env.exampleсо списком переменных без значений — для документации. - Проверяй обязательные переменные на старте и падай с понятной ошибкой, если их нет.
Итоги
Конфигурацию отделяют от кода: .env в разработке, реальные переменные окружения в проде, а NODE_ENV переключает режимы. Теперь приложение настраиваемо и не хранит секретов в репозитории. В финальном уроке разберём саму выкатку в продакшен.
Двенадцать факторов и конфиг
Принцип "конфигурация — в окружении" не выдумка, а один из пунктов методологии Twelve-Factor App, на которую опираются современные облачные сервисы. Идея проста: код приложения должен быть одинаков для всех окружений, а различия (адрес базы, ключи, режим) задаются снаружи через переменные. Тогда один и тот же артефакт без пересборки едет из теста в стейджинг и в прод — меняется только набор переменных. Это даёт воспроизводимость, упрощает откаты и делает невозможной случайную утечку прод-секрета в репозиторий. Файл .env.example с перечнем нужных переменных без значений служит живой документацией контракта между кодом и окружением.