Пошук уроків, статей та іншого контенту
Дізнаємося, як передавати налаштування через змінні середовища та читати їх у Node.js.
Змінні середовища — це значення, які операційна система передає процесу під час його запуску. Вони дають змогу зберігати налаштування окремо від коду програми.
Це корисно, коли одна й та сама програма запускається в різних середовищах:
локально;
на тестовому сервері;
у production;
у контейнері або CI-системі.
Наприклад, порт сервера, режим роботи або адреса зовнішнього сервісу можуть відрізнятися залежно від середовища. Немає потреби змінювати код — достатньо передати інші змінні.
У Node.js доступ до змінних середовища надає об’єкт process.env.
Розглянемо простий приклад:
const port = process.env.PORT;
console.log(`Значення PORT: ${port}`);Якщо змінна PORT не визначена, результатом буде undefined.
Змінні середовища завжди читаються як рядки:
console.log(typeof process.env.PORT); // "string"Навіть якщо передати число 3000, Node.js отримає значення "3000". Для використання такого значення як числа його потрібно перетворити.
У Linux та macOS змінну можна передати перед командою запуску:
PORT=4000 node app.jsУ Windows PowerShell:
$env:PORT = "4000"
node app.jsУ Windows Command Prompt:
set PORT=4000
node app.jsТака змінна буде доступна тільки процесу Node.js і процесам, які він запустить.
Для необов’язкових налаштувань зручно використовувати значення за замовчуванням:
const port = process.env.PORT ?? "3000";
const host = process.env.HOST ?? "127.0.0.1";
console.log(`Сервер: http://${host}:${port}`);Оператор ?? використовує праве значення лише тоді, коли ліве дорівнює null або undefined.
Це відрізняється від оператора ||:
const valueWithNullish = process.env.VALUE ?? "default";
const valueWithOr = process.env.VALUE || "default";|| також замінить порожній рядок, а ?? — ні. Для змінних середовища часто важливо окремо вирішити, чи є порожній рядок допустимим значенням.
Оскільки всі змінні є рядками, логічні та числові значення потрібно обробляти явно.
Наприклад, такий код може дати несподіваний результат:
const debug = Boolean(process.env.DEBUG);
console.log(debug);Рядок "false" є непорожнім, тому Boolean("false") поверне true.
Краще створити явне перетворення:
const debug = process.env.DEBUG === "true";Числові значення також потрібно перевіряти:
const port = Number(process.env.PORT ?? "3000");
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error("PORT має бути цілим числом від 1 до 65535");
}Перевірка важлива, тому що помилка в конфігурації має бути помітною під час запуску, а не проявлятися пізніше у вигляді незрозумілої помилки.
Створимо HTTP-сервер, налаштування якого передаються через змінні середовища.
const http = require("node:http");
const portValue = process.env.PORT ?? "3000";
const host = process.env.HOST ?? "127.0.0.1";
const environment = process.env.NODE_ENV ?? "development";
const debug = process.env.DEBUG === "true";
const port = Number(portValue);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error("PORT має бути цілим числом від 1 до 65535");
}
const server = http.createServer((request, response) => {
const result = {
message: "Сервер працює",
environment,
debug,
};
response.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
});
response.end(JSON.stringify(result));
});
server.listen(port, host, () => {
console.log(`Сервер запущено: http://${host}:${port}`);
});Збережімо код у файлі app.js і запустімо його з налаштуваннями:
PORT=4000 HOST=0.0.0.0 NODE_ENV=production DEBUG=true node app.jsУ консолі з’явиться:
Сервер запущено: http://0.0.0.0:4000Якщо не передати жодної змінної, використаються значення за замовчуванням:
node app.jsУ цьому випадку сервер запуститься на 127.0.0.1:3000 у режимі development.
.envПід час локальної розробки змінні часто зберігають у файлі .env:
PORT=4000
HOST=127.0.0.1
NODE_ENV=development
DEBUG=trueNode.js підтримує завантаження такого файлу через параметр --env-file, починаючи з версії Node.js 20.6:
node --env-file=.env app.jsПісля запуску значення з .env будуть доступні через process.env.
Файл .env не читається автоматично лише тому, що він існує в корені проєкту. Потрібно або явно використати --env-file, або налаштувати інший спосіб завантаження.
Файли з реальними секретами не слід додавати до системи контролю версій. Зазвичай .env додають до .gitignore:
.envНатомість у репозиторії можна зберігати приклад без секретних значень, наприклад .env.example:
PORT=3000
HOST=127.0.0.1
NODE_ENV=development
DEBUG=falseЗмінні середовища часто використовують для:
ключів API;
паролів до бази даних;
токенів доступу;
секретів для підписування.
Наприклад:
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("Не задано DATABASE_URL");
}Не варто:
записувати секрети безпосередньо в код;
додавати .env із секретами до репозиторію;
виводити всі змінні через console.log(process.env);
передавати секрети в повідомлення про помилки або відповіді API.
Змінні середовища не є автоматичним шифруванням. Будь-який процес або інструмент, який має відповідний доступ до середовища виконання, потенційно може їх прочитати.
Якщо читати process.env у багатьох файлах, конфігурація стає складною для контролю. Краще зібрати налаштування в одному модулі.
config.js:
function readPort(value) {
const port = Number(value ?? "3000");
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error("PORT має бути цілим числом від 1 до 65535");
}
return port;
}
function readBoolean(value, defaultValue = false) {
if (value === undefined) {
return defaultValue;
}
if (value === "true") {
return true;
}
if (value === "false") {
return false;
}
throw new Error("Булева змінна має бути true або false");
}
module.exports = {
port: readPort(process.env.PORT),
host: process.env.HOST ?? "127.0.0.1",
environment: process.env.NODE_ENV ?? "development",
debug: readBoolean(process.env.DEBUG),
};app.js:
const http = require("node:http");
const config = require("./config");
const server = http.createServer((request, response) => {
response.writeHead(200, {
"Content-Type": "application/json; charset=utf-8",
});
response.end(
JSON.stringify({
message: "Сервер працює",
environment: config.environment,
debug: config.debug,
}),
);
});
server.listen(config.port, config.host, () => {
console.log(`Сервер запущено: http://${config.host}:${config.port}`);
});Переваги такого підходу:
усі налаштування зібрані в одному місці;
перевірка виконується під час запуску;
решта коду працює з уже підготовленими значеннями;
легше замінити конфігурацію під час тестування.
const port = process.env.PORT + 1;Якщо PORT дорівнює "3000", результатом буде рядок "30001".
Правильно:
const port = Number(process.env.PORT ?? "3000") + 1;const debug = Boolean(process.env.DEBUG);Для DEBUG=false результатом буде true, оскільки "false" — непорожній рядок.
Правильно:
const debug = process.env.DEBUG === "true";Якщо програма потребує секретного ключа, не варто мовчки використовувати порожнє значення:
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error("API_KEY є обов’язковою змінною середовища");
}.envФайл .env сам по собі не змінює process.env. Для вбудованої підтримки Node.js потрібно запустити програму з --env-file:
node --env-file=.env app.js.env до GitПеред комітом перевіряйте, що .env ігнорується, а секретні значення не потрапили до репозиторію.
Змінні середовища доступні в Node.js через process.env.
Усі їхні значення мають рядковий тип.
Числа та логічні значення потрібно перетворювати й перевіряти вручну.
Для необов’язкових налаштувань варто задавати значення за замовчуванням.
Обов’язкові змінні слід перевіряти під час запуску.
Файл .env можна завантажити в сучасному Node.js через --env-file.
Секрети не потрібно зберігати в коді або додавати до системи контролю версій.
Централізація конфігурації спрощує підтримку програми.