Пошук уроків, статей та іншого контенту
Налаштуйте змінні середовища, секрети та конфігурацію застосунку для різних середовищ без зберігання чутливих даних у коді.
Конфігурація — це значення, які змінюють поведінку застосунку без зміни його коду. Наприклад:
порт, на якому запускається сервер;
режим роботи: розробка чи продакшн;
адреса зовнішнього сервісу;
ключі API;
паролі та інші секрети.
Одному й тому самому коду можуть відповідати різні конфігурації:
локально застосунок працює на порту 3000;
на тестовому сервері — на порту 4000;
у продакшні значення порту та ключів надає інфраструктура.
Зберігати такі значення безпосередньо в коді незручно й небезпечно. Для цього використовують змінні середовища.
Змінні середовища доступні в Node.js через об’єкт process.env:
console.log(process.env.NODE_ENV);
console.log(process.env.PORT);Значення змінних середовища завжди є рядками або undefined, якщо змінну не встановлено.
Наприклад, запустити програму можна так:
PORT=3000 NODE_ENV=development node app.jsУ Windows PowerShell команда має такий вигляд:
$env:PORT="3000"
$env:NODE_ENV="development"
node app.jsПісля завершення процесу ці значення зникають. Для постійнішого локального налаштування часто використовують файл .env.
.envФайл .env містить змінні у форматі ІМ’Я=значення:
PORT=3000
NODE_ENV=development
DATABASE_URL=postgres://localhost:5432/shop
API_KEY=local-secretФайл .env призначений для локального запуску. Він може містити секрети, тому його зазвичай не додають до репозиторію.
Додайте .env до .gitignore:
.envСучасні версії Node.js можуть завантажити такий файл без додаткової бібліотеки:
node --env-file=.env app.jsПісля цього значення з .env будуть доступні через process.env.
Якщо ваша версія Node.js не підтримує параметр
--env-file, для роботи з.envможна використати спеціальну бібліотеку, але в цьому уроці достатньо вбудованих можливостей Node.js.
Не варто звертатися до process.env у кожному файлі застосунку. Краще один раз прочитати й перевірити конфігурацію в окремому модулі.
Створимо файл config.js:
const portValue = process.env.PORT ?? "3000";
const port = Number(portValue);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error("PORT має бути цілим числом від 1 до 65535");
}
const nodeEnv = process.env.NODE_ENV ?? "development";
const allowedEnvironments = ["development", "test", "production"];
if (!allowedEnvironments.includes(nodeEnv)) {
throw new Error(
`NODE_ENV має бути одним із: ${allowedEnvironments.join(", ")}`
);
}
const apiKey = process.env.API_KEY;
if (nodeEnv === "production" && !apiKey) {
throw new Error("У production потрібно вказати API_KEY");
}
module.exports = {
port,
nodeEnv,
apiKey,
};Тепер основний файл app.js використовує вже підготовлену конфігурацію:
const http = require("node:http");
const config = require("./config");
const server = http.createServer((request, response) => {
response.setHeader("Content-Type", "application/json; charset=utf-8");
response.end(
JSON.stringify({
message: "Застосунок працює",
environment: config.nodeEnv,
})
);
});
server.listen(config.port, () => {
console.log(
`Сервер запущено на http://localhost:${config.port} у режимі ${config.nodeEnv}`
);
});Створіть файл .env:
PORT=3000
NODE_ENV=development
API_KEY=local-only-keyЗапустіть застосунок:
node --env-file=.env app.jsПісля запуску відкрийте в браузері адресу http://localhost:3000.
Цей приклад можна запустити без сторонніх бібліотек у версії Node.js із підтримкою --env-file.
Для необов’язкових змінних можна визначити значення за замовчуванням:
const port = Number(process.env.PORT ?? "3000");
const nodeEnv = process.env.NODE_ENV ?? "development";Оператор ?? використовує значення праворуч лише тоді, коли значення ліворуч дорівнює null або undefined.
Значення за замовчуванням підходять для безпечних налаштувань, наприклад локального порту. Для секретів краще не вигадувати значення за замовчуванням:
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error("API_KEY не налаштовано");
}Так застосунок завершить роботу одразу й повідомить про проблему, замість того щоб працювати з неправильними параметрами.
Усі значення з process.env є рядками. Тому їх потрібно перетворювати на потрібний тип.
const timeout = Number(process.env.TIMEOUT_MS ?? "5000");
if (!Number.isFinite(timeout) || timeout <= 0) {
throw new Error("TIMEOUT_MS має бути додатним числом");
}Не слід перевіряти логічну змінну лише через її наявність:
if (process.env.DEBUG) {
// Цей код виконається навіть для DEBUG=false,
// оскільки рядок "false" є непорожнім.
}Рядок "false" у JavaScript є істинним значенням. Перетворюйте його явно:
const debug = process.env.DEBUG === "true";Тепер:
DEBUG=true перетвориться на true;
DEBUG=false перетвориться на false;
відсутнє значення також дасть false.
Для різних середовищ можна передавати різні змінні:
NODE_ENV=development node app.jsNODE_ENV=test node app.jsNODE_ENV=production node app.jsУ продакшні секрети зазвичай задаються не через файл, а засобами платформи розгортання або операційної системи. Наприклад, процес може отримати:
NODE_ENV=production PORT=8080 API_KEY=real-secret node app.jsКод при цьому залишається однаковим. Змінюються лише значення конфігурації.
Розділяйте:
development — локальна розробка;
test — автоматичні тести;
production — реальний застосунок.
Не покладайтеся на режим без перевірки. Якщо застосунок має працювати лише з відомими режимами, перевіряйте значення під час запуску.
.env.exampleЩоб інші розробники знали, які змінні потрібні застосунку, створіть .env.example:
PORT=3000
NODE_ENV=development
API_KEY=вкажіть-локальний-ключЦей файл не повинен містити справжніх секретів. Його можна додати до репозиторію.
Типовий порядок роботи:
Скопіювати .env.example у .env.
Заповнити локальні значення.
Запустити застосунок із файлом .env.
Не додавати .env до Git.
Наприклад:
cp .env.example .env
node --env-file=.env app.jsДо секретів належать:
паролі;
ключі API;
токени доступу;
ключі шифрування;
рядки підключення до приватних баз даних.
Дотримуйтеся таких правил:
не записуйте секрети безпосередньо в JavaScript-код;
не додавайте файл .env до репозиторію;
не вставляйте справжні секрети у .env.example;
не виводьте секрети через console.log;
використовуйте різні секрети для різних середовищ;
якщо секрет потрапив у репозиторій, вважайте його скомпрометованим і замініть.
Змінні середовища не шифрують секрети автоматично. Вони лише дозволяють передати значення процесу окремо від коду. Захист доступу до цих змінних залежить від операційної системи та платформи розгортання.
Погано:
const apiKey = "real-production-secret";Краще:
const apiKey = process.env.API_KEY;Погано:
connectToDatabase(process.env.DATABASE_URL);Якщо змінну не встановлено, помилка може виникнути далеко від місця конфігурації.
Краще перевірити її під час запуску:
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL є обов’язковою змінною");
}Погано:
const port = process.env.PORT + 1;Якщо PORT дорівнює "3000", результатом буде рядок "30001".
Правильно:
const port = Number(process.env.PORT) + 1;.env до GitПеред комітом перевірте, що .env є в .gitignore. Якщо секрет уже було опубліковано, просте видалення файлу з наступного коміту не робить секрет безпечним: він може залишитися в історії Git.
Не виводьте весь process.env:
console.log(process.env);У ньому можуть бути паролі та токени. Логуйте лише безпечні значення, наприклад назву середовища й порт.
Конфігурація описує налаштування, які можуть змінюватися між середовищами.
У Node.js змінні середовища доступні через process.env.
Значення з process.env завжди є рядками, тому числа й логічні значення потрібно перетворювати.
Обов’язкові змінні слід перевіряти під час запуску.
Локальні значення можна зберігати у .env, а .env потрібно додати до .gitignore.
Файл .env.example документує потрібні змінні без справжніх секретів.
Секрети не можна зберігати в коді або в репозиторії.
Конфігурацію зручно читати й перевіряти в окремому модулі.