Node.js 26: Temporal API, Map Upserts oraz Undici 8 wyjaśnione
Wyjaśnia główne zmiany w Node.js 26 skierowane do warstwy backendowej, w tym stabilną API Temporal, wbudowane metody modyfikacji tablic Map, poprawy wydajności dzięki Undici 8 oraz zmiany mogące powodować problemy, które należy sprawdzić przed aktualizacją.
Źródła: Node.js 26 Just Changed the Backend Runtime — Here’s What Actually Matters
Każda ważna wersja Node.js dostarczana jest z długim wykazem zmian, podwyżek wersji oraz przyjaznymi notatkami wydawniczymi. Większość zespołów rzuci okiem na listę zmian, wzruszy ramionami i odłoży aktualizację na rok lub dwa.
Node.js 26 łamie ten schemat.
To wydanie wykracza poza wewnętrzne ulepszenia; zmienia sposób pisania kodu backendowego na co dzień. Usuwa zależności od dostawców trzecich, na które zespoły polegały przez lata, porzuca przestarzałe wzorce, które potajemnie spowalniały systemy produkcyjne, oraz doprowadza środowisko wykonawcze do bardziej ścisłego dostosowania do nowoczesnych standardów internetowych.
Jeśli zarządzasz usługami Node.js w środowisku produkcyjnym, oto co naprawdę ma znaczenie, dlaczego wpływa to na twój codzienny proces pracy oraz jakie pułapki należy unikać przed aktualizacją.
1. Śmierć obiektu Date: stabilna API czasowa
Przez około trzy dekady programiści JavaScript walczą z wbudowanym obiektem Date. Jest on zmienny, miesiące są indeksowane od zera, parsowanie ciągów znakowych zachowuje się niespójnie na różnych platformach, a obsługa stref czasowych bez ciężkich bibliotek takich jak Moment, Luxon czy Day.js jest naprawdę uciążliwa.
Node.js 26 włącza Temporal API globalnie, domyślnie, bez konieczności używania flagi eksperymentalnej.
Dlaczego to ma znaczenie?
Temporal dostarcza niezmienne obiekty wartości uwzględniające strefy czasowe. Łączy on w spójny sposób dokładną chwilę w czasie z datą kalendarzową, godziną zegara ściennego oraz czasem trwania.
Znany błąd w planowaniu zadań na poziomie backendu dotyczy arytmetyki miesięcy. W wcześniejszych wersjach Node dodanie jednego miesiąca do 31 stycznia mogło spowodować przechodzenie na marzec, ponieważ luty nie ma tylu dni:
// 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 prawidłowo uwzględnia granice kalendarza:
// 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)
Dokładne daty i godziny w różnych strefach czasowych
Gdy zajmujesz się fakturowaniem, opłatami za subskrypcje lub ścieżkami audytowymi obejmującymi kilka regionów, potrzebujesz dokładnej chwili (Instant), którą można przekształcić na lokalny czas zegara ścianowego (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()}`);
Nie ma już potrzeby zwiększania rozmiaru pliku ani korzystania z pakietów od third party tylko po to, by obliczać cykle fakturowania lub wyświetlać daty dla różnych stref czasowych.
2. Natywne operacje dodawania i usuwania w Map: getOrInsert i getOrInsertComputed
Pamięci cache w pamięci komputera oraz liczniki ograniczające częstotliwość zapytań są podstawą inżynierii backendowej. Wcześniej, aby dodać klucz do Map tylko wtedy, gdy go brakowało, trzeba było pisać powtarzalny kod szablonowy:
// The old, redundant lookup pattern
if (!cache.has(userId)) {
cache.set(userId, fetchUserProfile(userId));
}
const profile = cache.get(userId);
To rozwiązanie zmusza do dwóch lub trzech oddzielnych wyszukiwań kluczy dla czegoś, co koncepcyjnie stanowi jedną operację.
Node.js 26 zawiera V8 14.6, który wprowadza standaryzowane metody Map upsert z TC39:
map.getOrInsert(key, defaultValue)map.getOrInsertComputed(key, computeFunction)
Praktyczny przykład: agregator zapytań w pamięci
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;
}
Funkcja przekazywana do getOrInsertComputed jest wykonywana w sposób opóźniony — uruchamia się tylko wtedy, gdy klucz jeszcze nie istnieje. Dzięki temu unika się bezcelowego tworzenia obiektów i logika cache staje się prostsza we wszystkich usługach API.
3. Wysoka przepustowość HTTP z Undici 8
Główna funkcja Node fetch() działa na bazie Undici, a Node.js 26 aktualizuje silnik HTTP na Undici 8.
Co zmieniło się wewnątrz?
Starsze konfiguracje klientów HTTP miały tendencję do powstawania wąskich gardeł pod ciągłym obciążeniem, głównie z powodu kosztów związanych z pulą połączeń oraz nieefektywnego analizowania nagłówków. Undici 8 rozwiązuje ten problem poprzez:
- Zmniejszone zużycie pamięci przy transmisji odpowiedzi strumieniowych: duże pliki JSON lub pobierane pliki są teraz przekazywane w formie strumienia z znacznie mniejszą liczbą przerw spowodowanych zbieraniem śmieci.
- Lepsze wykorzystanie pipeline’ów HTTP/1.1 oraz pul połączeń HTTP/2: połączenia typu keep-alive są domyślnie utrzymywane bardziej agresywnie, co zmniejsza opóźnienia przy komunikacji z usługami poniżej w łańcuchu.
// 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
}
Jeśli twoja usługa pełni rolę bramy API lub kieruje żądania do kilku mikrousług poniżej w łańcuchu, to ulepszenie na poziomie silnika zmniejsza opóźnienia w procentylach p95/p99 bez konieczności wprowadzania jakichkolwiek zmian w kodzie aplikacji.
4. Uruchamianie TypeScript bez obciążenia transpilacją
Node.js stopniowo zmienia sposób ładowania modułów. Od wersji Node.js 26 usuwanie typów jest w pełni obsługiwaną, stabilną funkcją, a wcześniejsze flagi eksperymentalne służące do włączania transformacji zostały całkowicie usunięte.
Teraz możesz uruchomić plik .ts bezpośrednio z linii poleceń:
node src/server.ts
Co faktycznie obejmuje ta funkcja
Kluczowe jest zachowanie wyraźnej różnicy pomiędzy usuwaniem typów a weryfikacją typów:
Your TypeScript File
│
▼
[ Node.js Type Stripper ] ──> Removes interfaces, types, & generics in memory
│
▼
[ Pure V8 Engine Execution ] ──> Runs the resulting JavaScript instantly
- Nie przeprowadza weryfikacji typów. Jeśli twój kod przypisze wartość liczbową zmiennej typowanej jako ciąg znaków, Node.js będzie ją chętnie uruchamiał bez żadnych zastrzeżeń. Nadal potrzebujesz flagi
tsc --noEmitw swoim pipeline CI/CD, aby wykryć tego typu błędy przed ich wdrożeniem.
constructor(public name: string)) nie są obsługiwane przez ten narzędzie i powodują błąd.Dla usług, narzędzi linii poleceń oraz procesów w tle zbudowanych przy użyciu standardowej składni TypeScript oznacza to, że możesz usunąć tsx, ts-node lub dedykowany proces monitorowania budowy z swojego lokalnego środowiska rozwoju.
5. Zmiany podważające, które musisz sprawdzić
Ponieważ Node.js 26 to znaczna aktualizacja wersji, administratorzy wykorzystali tę okazję, aby usunąć stare API, które były używane od dawna.
+------------------------------------+------------------------------------------+
| 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) |
+------------------------------------+------------------------------------------+
Ukryte ryzyko w mechanizmach strumieniowania
Prawdopodobnie najbardziej ryzykowną zmianą w tym wydaniu jest usunięcie prywatnych podmodułów streamowych — _stream_readable, _stream_writable, _stream_transform oraz podobnych plików wewnętrznych.
Wiele starszych pakietów na npm, szczególnie tych, których nikt nie modyfikował od czasów przed 2020 rokiem, omija publiczną interfejs require('stream') i bezpośrednio korzysta z tych elementów wewnętrznych:
// BROKEN in Node.js 26: Throws ERR_MODULE_NOT_FOUND
const Readable = require('_stream_readable');
// CORRECT:
const { Readable } = require('node:stream');
Jeśli choćby jeden element zależności ukryty głęboko w drzewie zależności nadal odwołuje się do modułów _stream_*, twoja aplikacja nie uruchomi się od razu po starcie. Zanim spróbujesz dokonać aktualizacji, sprawdź swój plik lockfile pod kątem tego wzorca:
npm ls _stream_readable
Jeśli audyt wykryje jakieś problemy, będziesz musiał zaktualizować lub zastąpić tę zależność, zanim będziesz mógł kontynuować.
Czy powinieneś teraz aktualizować swoje systemy produkcyjne?
Node.js stosuje wzorzec wydawania wersji naprzemiennie dla parzystych i nieparzystych numerów głównych wersji:
- Wydania o nieparzystych numerach (23, 25): wersje krótkotrwałe przeznaczone do testowania; nigdy nie stają się wersjami LTS.
- Wydania o parzystych numerach (22, 24, 26): wersje przeznaczone do użycia w środowiskach produkcyjnych.
Node.js 26 stał się wersją „bieżącą” w maju i ma wejść w tryb aktywnego długoterminowego wsparcia pod koniec października.
[May 2026: Node 26 Current] ────> [October 2026: Node 26 LTS] ────> [Production Default]
▲ ▲
Test in Staging/CI Migrate Production Workloads
Praktyczny plan wdrażania
- Pozostaw na razie wdrożenie do krytycznych systemów produkcyjnych. Daj wersji „bieżącej” czas na dojrzałe funkcjonowanie, aż oficjalnie stanie się wersją LTS w październiku. Jeśli musisz natychmiast zaktualizować środowisko produkcyjne, Node.js 24 pozostaje obecną aktywną opcją LTS.
NODE_MODULE_VERSION 147, które będą wymagać ponownego skompilowania.Temporal.PlainDate i Temporal.Instant, unikając jednocześnie wprowadzania bibliotek trzecich do obsługi dat w nowych projektach.Podsumowanie
Główny cel wprowadzenia Node.js 26 jest prosty: zmniejszenie oporów w pracy oraz spłata lat gromadzonego długu technologicznego.
Od arytmetyki dat zarówno w wersji natywnej, jak i niezawodnej dzięki Temporal, przez bardziej efektywne zarządzanie powszechnymi strukturami danych za pomocą operacji Map upsert, wbudowane narzędzia do oczyszczania kodu w TypeScript oraz szybszą przepustowość HTTP dzięki Undici 8, to aktualizacja środowiska programistycznego umożliwia szybsze tworzenie systemów backendowych przy jednoczesnym korzystaniu z mniejszej liczby pakietów zewnętrznych.
Node.js znacznie dojrzał. Era konieczności używania tuzina bibliotek pomocniczych tylko po to, by pracować z datą kalendarzową lub dopasować wzorzec ścieżki, jest obecnie w dużej mierze za nami.