Как работать с переменными окружения и dotenv в Node.js?
process.env. Пакет dotenv позволяет загружать их из файла .env в локальной разработке, вызвав dotenv.config() в начале приложения.Переменные окружения в Node.js
Переменные окружения — это пары «ключ-значение», которые задаются на уровне операционной системы или CI/CD-платформы и позволяют разделить конфигурацию и код. В Node.js они доступны через глобальный объект process.env.
console.log(process.env.NODE_ENV); // 'production', 'development' и т.д.
console.log(process.env.PORT); // '3000'
Важно: все значения process.env являются строками (или undefined), поэтому числа и булевы значения нужно преобразовывать явно.
Пакет dotenv
В локальной разработке удобно хранить переменные в файле .env в корне проекта. Пакет dotenv читает этот файл и добавляет переменные в process.env.
Установка
npm install dotenv
Использование
Вызов dotenv.config() должен быть самым первым в точке входа приложения — до любых импортов, которые используют process.env.
// Файл: src/index.js
require('dotenv').config();
const express = require('express');
const app = express();
const PORT = Number(process.env.PORT) || 3000;
app.listen(PORT, () => console.log(`Сервер запущен на порту ${PORT}`));
Файл .env
PORT=3000
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=secret
JWT_SECRET=my_super_secret_key
Безопасность
- Файл
.envникогда не коммитится в репозиторий. Добавьте его в.gitignore. - Создайте
.env.exampleс ключами без значений — как шаблон для новых разработчиков. - В production переменные задаются напрямую через платформу (Heroku, AWS, Docker, GitHub Actions) без использования dotenv.
Структура конфига
Хорошая практика — централизовать чтение переменных в отдельном модуле config.js, чтобы не обращаться к process.env напрямую по всему коду.
// Файл: src/config.js
require('dotenv').config();
module.exports = {
port: Number(process.env.PORT) || 3000,
db: {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
},
jwtSecret: process.env.JWT_SECRET,
};
// Файл: src/index.js
const config = require('./config');
console.log(config.port); // 3000
Разные окружения
Для разделения конфигураций по окружениям используют несколько .env-файлов:
.env— значения по умолчанию.env.development— для разработки.env.production— для продакшена (не коммитится)
Пакет dotenv поддерживает указание пути к файлу:
require('dotenv').config({ path: `.env.${process.env.NODE_ENV}` });
Валидация переменных
Для critical-конфигурации используют схему-валидатор (например, пакет joi или zod), чтобы приложение не запустилось при отсутствии обязательных переменных.
const requiredVars = ['DB_USER', 'DB_PASSWORD', 'JWT_SECRET'];
requiredVars.forEach((key) => {
if (!process.env[key]) {
throw new Error(`Отсутствует обязательная переменная окружения: ${key}`);
}
});
Что хочет услышать интервьюер
Кандидат знает, что переменные окружения доступны через process.env и что все значения — строки
Кандидат понимает, зачем нужен dotenv и как его инициализировать в самом начале точки входа
Кандидат осознаёт, что .env нельзя коммитить, и упоминает .gitignore и .env.example
Кандидат понимает разницу между локальной разработкой (dotenv) и production (переменные платформы)
Кандидат знает, что числа и булевы значения нужно явно приводить к нужному типу
Пример: Централизованный конфиг с валидацией на TypeScript
// .env
// PORT=3000
// DB_URL=postgres://localhost:5432/mydb
// JWT_SECRET=super_secret
// src/config.ts
import 'dotenv/config'; // современный способ — без вызова .config()
const config = {
port: Number(process.env.PORT) || 3000,
dbUrl: process.env.DB_URL as string,
jwtSecret: process.env.JWT_SECRET as string,
};
// Валидация обязательных переменных при старте
const required: Array<keyof typeof config> = ['dbUrl', 'jwtSecret'];
for (const key of required) {
if (!config[key]) {
throw new Error(`Отсутствует обязательная переменная: ${key}`);
}
}
export default config;
Пример: Валидация через zod (продвинутый вариант)
// Пример с zod для строгой типизации конфига
import { z } from 'zod';
import 'dotenv/config';
const envSchema = z.object({
PORT: z.string().transform(Number).default('3000'),
DB_URL: z.string().url(),
JWT_SECRET: z.string().min(16),
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
});
// parse бросит ошибку с подробным описанием, если схема не совпадает
const env = envSchema.parse(process.env);
export default env;
Типичные ошибки
Коммит файла .env в репозиторий с реальными секретами
Вызов dotenv.config() после импортов, которые уже читают process.env — переменные не успевают загрузиться
Использование dotenv в production вместо переменных платформы
Обращение к process.env напрямую по всему коду вместо централизованного config-модуля
Забывают, что process.env возвращает строку, и сравнивают PORT === 3000 вместо Number(PORT) === 3000


