Node.js 26: пояснення Temporal API, Map Upserts та Undici 8
Пояснює основні зміни в Node.js 26, спрямовані на роботу з бекендом, включаючи стабільний API Temporal, вбудовані методи обробки даних у Map, покращення продуктивності завдяки Undici 8, а також зміни, які можуть спричинити проблеми під час оновлення, що потребують перевірки.
Джерела: Node.js 26 Just Changed the Backend Runtime — Here’s What Actually Matters
Кожна значуща версія Node.js постачається з довгим списком змін, підвищень версій та приємних приміток до випуску. Більшість команд лише кидають погляд на журнал змін, знизують плечима та відкладають оновлення на рік чи два.
Node.js 26 порушує цю тенденцію.
Цей випуск виходить за межі внутрішнього вдосконалення — він змінює спосіб написання коду для бекенду щодня. Він видаляє сторонні залежності, на які команди покладалися роками, скасовує застарілі підходи, які тихо уповільнювали роботу продуктивних систем, та просуває середовище виконання до більш суворої відповідності сучасним веб-стандартам.
Якщо ви керуєте сервісами на Node.js у продакшені, ось що справді має значення, чому це впливає на ваш щоденний робочий процес та які підводні камені потрібно враховувати перед оновленням.
1. Смерть об’єкта Date: стабільний API для роботи з часом
Протягом приблизно трьох десятиліть розробники JavaScript боролися з вбудованим об’єктом Date. Він є змінним, місяці індексуються з нуля, розбір рядків поводиться по-різному на різних платформах, а робота з часовими поясами без важких бібліотек на кшталт Moment, Luxon чи Day.js справді є складною.
Node.js 26 увімкнув Temporal API глобально за замовчуванням, без необхідності використання експериментальних флагів.
Чому це важливо?
Temporal надає вам незмінні об’єкти значень, які враховують часовий пояс. Він чітко розділяє точний момент у часі від календарної дати, часу з годинника та тривалості.
Відома проблема з плануванням у бекенді пов’язана з арифметикою місяців. У ранніх версіях Node додавання одного місяця до 31 січня могло призвести до березня, оскільки у лютому не так багато днів:
// Legacy JavaScript Date (Before Node.js 26)
const oldDate = new Date('2026-01-31T00:00:00Z');
oldDate.setMonth(oldDate.getMonth() + 1);
console.log(oldDate.toISOString());
// Output: 2026-03-03T00:00:00.000Z (Silently overflows into March!)
Temporal належним чином дотримується меж календаря:
// Node.js 26: Clean and predictable
const today = Temporal.PlainDate.from('2026-01-31');
const nextMonth = today.add({ months: 1 });
console.log(nextMonth.toString());
// Output: 2026-02-28 (Clamps correctly to the last day of February)
Точні часові позначки у різних часових зонах
Коли ви працюєте з рахунками-фактурами, оплатою підписок або історією аудиту, що охоплює кілька регіонів, вам потрібна точна мить (Instant), яку можна перетворити на місцевий час з годинником (ZonedDateTime):
// Current exact point in time (UTC)
const exactInstant = Temporal.Now.instant();
// Convert to a specific time zone seamlessly
const tokyoTime = exactInstant.toZonedDateTimeISO('Asia/Tokyo');
const londonTime = exactInstant.toZonedDateTimeISO('Europe/London');console.log(`Tokyo: ${tokyoTime.toString()}`);
console.log(`London: ${londonTime.toString()}`);
Більше немає потреби збільшувати розмір пакета чи використовувати сторонні бібліотеки лише для обчислення циклів оплати чи відображення дат у різних часових зонах.
2. Вбудовані операції вставки/заміни в Map: getOrInsert та getOrInsertComputed
Кеші в пам’яті та лімітуючі засоби є основою інженерії бекенду. Раніше для вставки ключа в Map лише тоді, коли його не було, доводилося писати повторюваний шаблон коду:
// The old, redundant lookup pattern
if (!cache.has(userId)) {
cache.set(userId, fetchUserProfile(userId));
}
const profile = cache.get(userId);
Ця схема змушує виконувати два чи три окремі пошуки ключа для того, що концептуально є однією операцією.
Node.js 26 постачається з V8 14.6, який включає стандартизовані методи Map upsert від TC39:
map.getOrInsert(key, defaultValue)map.getOrInsertComputed(key, computeFunction)
Практичний приклад: Агрегатор запитів у пам’яті
const requestBuckets = new Map();
function trackUserRequest(userId, payload) {
// If userId is missing, execute the callback and insert the array atomically
const userQueue = requestBuckets.getOrInsertComputed(userId, () => []);
userQueue.push(payload);
return userQueue.length;
}
Функція, яку ви передаєте до getOrInsertComputed, виконується з відстрочкою — вона запускається лише тоді, коли ключ ще не існує. Це уникає марної виділення об’єктів та спрощує логіку кешування в різних API-сервісах.
3. Висока пропускна здатність HTTP за допомогою Undici 8
Глобальна функція fetch() в Node працює на основі Undici, а Node.js 26 оновлює базовий двигун HTTP до Undici 8.
Що змінилося „під капотом“?
Старіші конфігурації клієнтів HTTP схильні до уповільнення під постійною навантажувальною силою, переважно через надмірну витрату ресурсів на кешування з’єднань та неефективний аналіз заголовків. Undici 8 вирішує цю проблему за допомогою:
- Зниження використання пам’яті для потокових відповідей: великі об’єми даних у форматі JSON чи завантаження файлів тепер передаються у потоці з значно меншою кількістю перерв для очищення пам’яті.
- Краще повторне використання механізмів HTTP/1.1 та кешування з’єднань HTTP/2: за замовчуванням з’єднання типу keep-alive підтримуються більш активно, що зменшує час виконання запитів до підсистем.
// Native fetch in Node.js 26 automatically inherits Undici 8 optimizations
const response = await fetch('https://api.internal.service/stream-data', {
headers: { 'Connection': 'keep-alive' }
});
for await (const chunk of response.body) {
processDataChunk(chunk); // Efficient backpressure and zero intermediate buffering
}
Якщо ваш сервіс виконує функцію API-шлюза чи розсилає запити до кількох підсистем, це оновлення на рівні двигуна знижує затримки на рівні p95/p99 без необхідності змінювати код вашого додатку.
4. Запуск TypeScript без навантаження від транспіляції
Node.js поступово змінює спосіб завантаження модулів. Починаючи з Node.js 26, видалення типів є повністю підтримуваною та стабільною функцією, а раніше використовувані експериментальні параметри для активації трансформації були повністю видалені.
Тепер ви можете запускати файл .ts безпосередньо з командного рядка:
node src/server.ts
Що насправді охоплює ця функція
Вкрай важливо чітко розмежовувати видалення типів та перевірку типів:
Your TypeScript File
│
▼
[ Node.js Type Stripper ] ──> Removes interfaces, types, & generics in memory
│
▼
[ Pure V8 Engine Execution ] ──> Runs the resulting JavaScript instantly
- Вона не виконує перевірки типів. Якщо ваш код присвоює числове значення змінній, оголошеній як рядок, Node.js буде успішно її виконувати без жодних проблем. Вам все одно потрібно використовувати
tsc --noEmitу вашому процесі CI/CD, щоб виявити такі помилки до розгортання.
constructor(public name: string)) не підтримуються цим інструментом та спричинять помилку.Для сервісів, інструментів командного рядка та фонових процесів, створених за допомогою стандартного синтаксису TypeScript, це означає, що ви можете вилучити tsx, ts-node або спеціалізований процес відстеження змін із вашої локальної налаштування розробки.
5. Зміни, які потребують перевірки
Оскільки Node.js 26 є значною версійною оновленням, розробники скористалися цією можливістю, щоб видалити API легасі, які існували протягом тривалого часу.
+------------------------------------+------------------------------------------+
| Removed API | Modern Replacement |
+------------------------------------+------------------------------------------+
| http.Server.prototype.writeHeader | res.writeHead() |
| _stream_readable, _stream_writable | require('node:stream').Readable / Writable|
| --experimental-transform-types | Native type-stripping (built-in) |
+------------------------------------+------------------------------------------+
Прихований ризик у внутрішніх механізмах потоків
Мабуть, найбільш ризикованою зміною в цьому випуску є видалення приватних підмодулів потоків — _stream_readable, _stream_writable, _stream_transform та подібних внутрішніх файлів.
Численні старі пакети в npm, особливо ті, якими не користувалися з часів до 2020 року, ігнорують публічний інтерфейс require('stream') та безпосередньо звертаються до цих внутрішніх елементів:
// BROKEN in Node.js 26: Throws ERR_MODULE_NOT_FOUND
const Readable = require('_stream_readable');
// CORRECT:
const { Readable } = require('node:stream');
Якщо навіть один з компонентів, розташованих глибоко в вашій ієрархії залежностей, все ще посилається на модулі _stream_*, ваша програма негайно зазнає помилки під час запуску. Перш ніж приступати до оновлення, перевірте свій файл lockfile на наявність такої структури:
npm ls _stream_readable
Якщо під час перевірки будуть виявлені проблеми, вам доведеться оновити або замінити цю залежність, перш ніж продовжувати роботу.
Чи варто вам оновлювати свої системи у продакшені зараз?
Node.js дотримується чергової схеми випуску між парними та непарними основними версіями:
- Непарні версії (23, 25): тимчасові версії, призначені для тестування; вони ніколи не стають LTS.
- Парні версії (22, 24, 26): версії, призначені для використання у продакшені.
У травні Node.js 26 став „текущою“ версією, і заплановано, що до кінця жовтня він отримає статус Active Long-Term Support.
[May 2026: Node 26 Current] ────> [October 2026: Node 26 LTS] ────> [Production Default]
▲ ▲
Test in Staging/CI Migrate Production Workloads
Практичний план впровадження
- Наразі відкладіть розгортання у критичних системах продакшену. Дайте „текущій“ версії час для дозрівання, поки вона офіційно не стане LTS у жовтні. Якщо вам потрібно негайно оновити середовище продакшену, Node.js 24 залишається поточним активним варіантом LTS.
NODE_MODULE_VERSION 147, які потребуватимуть перекомпіляції.Temporal.PlainDate та Temporal.Instant, уникаючи використання сторонніх бібліотек для обробки дат у нових проектах.Підсумок
Основна мета Node.js 26 є простою: зменшити перешкоди та погасити багаторічний борг застарілих технологій.
Від вбудованої та надійної арифметики дат через Temporal, до ефективнішої обробки поширених структур даних за допомогою операцій Map upsert, вбудованої функції очищення коду TypeScript та швидшого оброблення HTTP завдяки Undici 8, це оновлення runtime дозволяє створювати більш швидкі системи backend, використовуючи менше зовнішніх пакетів.
Node.js значно дорослішав. Епоха, коли потрібно було використовувати десятки допоміжних бібліотек лише для роботи з календарними датами чи підбору шаблонів маршрутів, наразі в основному минула.