Пошук уроків, статей та іншого контенту
Порівняйте середовища розробки та продакшен і визначте вимоги до надійності, безпеки та продуктивності Node.js-застосунку.
Середовище — це набір умов, у яких працює застосунок:
змінні оточення;
налаштування програми;
доступні сервіси;
рівень журналювання;
вимоги до безпеки та продуктивності.
Для Node.js-застосунку зазвичай розрізняють два основні середовища:
Development — локальна розробка;
Production — реальні користувачі та бойовий сервер.
Це не обов’язково два різні застосунки. Найчастіше це одна кодова база, яка працює з різними налаштуваннями.
Середовище розробки призначене для створення, перевірки та налагодження програми.
У ньому важливо:
швидко бачити помилки;
мати докладні повідомлення в консолі;
легко перезапускати сервер після зміни коду;
використовувати локальні бази даних і сервіси;
не витрачати зайві ресурси на оптимізацію.
Наприклад, у development можна показувати стек викликів помилки:
Error: Cannot connect to database
at connectToDatabase (database.js:12:9)
at startServer (server.js:8:3)Такий стек допомагає розробнику знайти проблему. Проте показувати його звичайному користувачу в production небезпечно, оскільки він може містити назви файлів, структуру проєкту або інші внутрішні дані.
Середовище production призначене для роботи реального застосунку.
У ньому важливі:
стабільність;
безпека;
передбачувана продуктивність;
контрольоване журналювання;
обробка помилок без розкриття внутрішніх деталей;
можливість відновити роботу після збою.
Production не означає, що помилки можна ігнорувати. Навпаки, їх потрібно записувати в журнали, але не показувати зайву інформацію користувачам.
NODE_ENVДля визначення середовища часто використовують змінну оточення NODE_ENV.
Типові значення:
development;
production;
test.
Node.js не змінює поведінку всього застосунку автоматично лише через NODE_ENV. Це значення читає ваш код або бібліотеки, які ви використовуєте.
Приклад запуску:
NODE_ENV=development node app.jsДля production:
NODE_ENV=production node app.jsУ Windows PowerShell синтаксис такий:
$env:NODE_ENV="development"
node app.jsЗмінні оточення доступні через process.env:
const environment = process.env.NODE_ENV || "development";
console.log(`Середовище: ${environment}`);
if (environment === "development") {
console.log("Увімкнено докладне журналювання");
}Вираз || "development" задає значення за замовчуванням, якщо NODE_ENV не встановлено.
Розглянемо невеликий HTTP-сервер. У development він показує деталі помилки, а в production повертає лише загальне повідомлення.
const http = require("node:http");
const environment = process.env.NODE_ENV || "development";
const port = Number(process.env.PORT) || 3000;
const server = http.createServer((request, response) => {
try {
if (request.url === "/error") {
throw new Error("Тестова внутрішня помилка");
}
response.writeHead(200, {
"Content-Type": "application/json; charset=utf-8"
});
response.end(JSON.stringify({
environment,
message: "Сервер працює"
}));
} catch (error) {
console.error(error);
response.writeHead(500, {
"Content-Type": "application/json; charset=utf-8"
});
const body = environment === "development"
? {
error: error.message,
stack: error.stack
}
: {
error: "Внутрішня помилка сервера"
};
response.end(JSON.stringify(body));
}
});
server.listen(port, () => {
console.log(`Сервер запущено на порту ${port}`);
});Збережіть код у файлі app.js і запустіть:
node app.jsПотім відкрийте шлях /error.
У development відповідь міститиме деталі помилки. У production запустіть сервер так:
NODE_ENV=production node app.jsТепер користувач отримає лише таку відповідь:
{
"error": "Внутрішня помилка сервера"
}При цьому повна помилка все одно буде записана в консоль сервера через console.error.
Надійний production-застосунок повинен передбачувано поводитися під час помилок.
До очікуваних помилок належать:
неправильні дані від користувача;
відсутній файл;
недоступний зовнішній сервіс;
невірний ідентифікатор ресурсу.
Такі помилки потрібно обробляти й повертати зрозумілу відповідь.
Не слід допускати, щоб одна помилка завершувала весь сервер без необхідності.
У production користувач не повинен бачити внутрішні деталі, але команда повинна мати змогу дослідити проблему.
Тому потрібно:
записувати помилки в журнали;
додавати час і контекст операції;
відстежувати недоступність залежностей;
перевіряти стан застосунку після запуску.
Якщо застосунку потрібна змінна оточення, краще повідомити про її відсутність одразу:
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("Не задано обов'язкову змінну DATABASE_URL");
}Так сервер не запуститься в неповністю налаштованому стані та не завершиться пізніше через менш зрозумілу помилку.
Відмінності між development і production особливо важливі для безпеки.
У production не варто повертати клієнту:
стек викликів;
текст внутрішніх винятків;
паролі та ключі;
рядки підключення до бази даних;
службові шляхи до файлів;
конфігурацію сервера.
Користувачу достатньо загального повідомлення та, за потреби, ідентифікатора помилки.
Секрети не потрібно записувати безпосередньо у файли JavaScript:
// Погано: секрет знаходиться в коді
const apiKey = "real-secret-key";Замість цього використовуйте змінну оточення:
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error("Не задано API_KEY");
}Файли з локальними секретами не повинні потрапляти до системи контролю версій. Для production секрети задають на сервері або через спеціальну систему керування секретами.
У development можуть використовуватися:
локальна база даних;
тестові ключі;
докладні повідомлення;
спрощена конфігурація.
У production мають використовуватися:
окремі облікові дані;
обмежені права доступу;
захищені секрети;
безпечні налаштування зовнішніх сервісів.
Під час розробки зручність часто важливіша за швидкість. У production навантаження може бути значно більшим.
Корисні відмінності:
не виводити надмірну кількість повідомлень у журнал;
не запускати інструменти налагодження без потреби;
не виконувати важкі діагностичні операції для кожного запиту;
використовувати production-режим бібліотек, якщо вони його підтримують;
заздалегідь підготувати необхідні ресурси.
Змінна NODE_ENV часто використовується бібліотеками для вибору production-режиму. Наприклад, бібліотека може вимкнути допоміжні перевірки або докладні повідомлення. Але конкретна поведінка залежить від цієї бібліотеки.
NODE_ENV=production не замінює оптимізацію коду. Він лише повідомляє застосунку та залежностям, у якому режимі вони працюють.
Зручний підхід — зберігати налаштування поза кодом.
Наприклад:
const config = {
environment: process.env.NODE_ENV || "development",
port: Number(process.env.PORT) || 3000,
logLevel: process.env.LOG_LEVEL || "info"
};
console.log(config);Один і той самий код можна запускати з різними параметрами:
NODE_ENV=development PORT=3000 LOG_LEVEL=debug node app.jsNODE_ENV=production PORT=8080 LOG_LEVEL=warn node app.jsЗначення змінних оточення зазвичай є рядками. Тому числові значення потрібно перетворювати явно:
const port = Number(process.env.PORT) || 3000;Також варто перевіряти коректність значення, а не лише його наявність:
const port = Number(process.env.PORT);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new Error("PORT має бути цілим числом від 1 до 65535");
}Перед розгортанням перевірте:
обробляються очікувані помилки;
сервер не завершується через некоректний запит;
обов’язкові змінні оточення перевіряються під час запуску;
помилки записуються в журнали;
застосунок можна перезапустити після збою.
NODE_ENV встановлено в production;
секрети не зберігаються в коді;
production-відповіді не містять стеків і внутрішніх деталей;
використовуються окремі production-облікові дані;
непотрібні діагностичні можливості вимкнено.
не виконується зайва діагностика;
рівень журналювання відповідає потребам;
застосунок перевірено під очікуваним навантаженням;
залежності встановлено в режимі, призначеному для production.
Зазвичай зміни проходять такі етапи:
Розробник створює функціональність у development.
Локально перевіряються успішні та помилкові сценарії.
Зміни перевіряються в тестовому середовищі.
Конфігурація production готується окремо від коду.
Застосунок запускається з NODE_ENV=production.
Після запуску перевіряються журнали та доступність сервісу.
Важливо, що код і конфігурація — це різні речі. Одна версія коду може використовувати різні порти, бази даних і секрети залежно від середовища.
Стек корисний під час розробки, але може розкрити внутрішню структуру програми в production.
Такий секрет легко випадково опублікувати разом із кодом. Використовуйте змінні оточення або спеціальні сховища секретів.
NODE_ENV усе налаштує автоматичноNODE_ENV — лише значення, яке потрібно врахувати в коді та залежностях. Воно не створює production-конфігурацію самостійно.
Експериментальний або помилковий код може змінити чи видалити реальні дані. Для розробки використовуйте окремі ресурси.
Якщо змінна відсутня, програма може запуститися з неправильним значенням і зламатися пізніше. Краще перевіряти обов’язкові налаштування під час старту.
Користувачам не потрібно показувати технічні деталі, але команді потрібні журнали для пошуку проблем. Налаштуйте відповідний рівень журналювання замість повного вимкнення.
Development призначений для швидкої розробки та налагодження.
Production призначений для стабільної та безпечної роботи реального застосунку.
Для визначення середовища часто використовують process.env.NODE_ENV.
У production не можна показувати користувачам стеки викликів, секрети та внутрішні дані.
Конфігурацію й секрети краще передавати через змінні оточення.
Надійний застосунок перевіряє обов’язкові налаштування, обробляє помилки та записує їх у журнали.
Production-режим може покращити поведінку залежностей, але не замінює якісну перевірку, безпеку та оптимізацію коду.