Next.js afișează date vechi? De ce Next.js 15 a renunțat la cache implicit
Next.js 15 nu mai pune implicit în cache fetch-urile, rutele GET și navigarea. De ce vechea regulă servea date vechi și cum verificați o aplicație Next.js 14.
Pe 23 mai 2024, echipa Next.js a publicat versiunea release candidate a Next.js 15 (în engleză) și, într-o secțiune numită „Caching updates”, a întors una dintre deciziile de la baza App Router. Cererile fetch, rutinele pentru rutele GET și navigarea din browser nu mai urmau să fie puse în cache în mod implicit. Formularea era prudentă: „Based on your feedback, we re-evaluated our caching heuristics” („Pe baza feedbackului vostru, am reevaluat regulile de cache”).
La doi ani distanță, citirea mai directă rămâne valabilă. Cache-ul implicit pentru date a fost o alegere greșită. Aplicațiile păreau rapide în demo și afișau, fără să se vadă, date greșite în producție. Costul îl plăteau tocmai cei care îl observau cel mai greu: programatorul care a moștenit aplicația și clientul care vedea prețurile de ieri. Un framework ar trebui să servească implicit date proaspete, iar cache-ul să fie o decizie pe care cineva o numește și și-o asumă. Dacă aveți în producție o aplicație Next.js 14 cu App Router, mai ales una scrisă de altcineva, merită să verificați unde servește date vechi fără să știe nimeni.
Ce făcea Next.js 14 implicit
Documentația de cache din Next.js 14 descrie deschis designul. Enumeră patru mecanisme de cache separate (în engleză): Request Memoization, Data Cache, Full Route Cache și Router Cache, cel din browser. Apoi spune regula: implicit, Next.js pune în cache cât de mult poate, ca să crească performanța și să scadă costurile, deci rutele sunt randate static, iar cererile de date sunt puse în cache, dacă nu renunțați explicit.
Concret, asta însemna:
- Cu configurația statică implicită, un
fetchpe server fără opțiuneacachefoloseaforce-cache(în engleză), iar rezultatul ajungea în Data Cache, care se păstrează și de la un deploy la altul. O versiune nouă nu îl golea. Funcțiile dinamice precumcookies()și configurația segmentului puteau schimba regula; lipsa opțiunii nu dovedește singură folosirea cache-ului. - O rută fără funcții dinamice (
cookies(),headers(), parametrii din URL) și fără cereri de date care ocolesc cache-ul era randată static, la build (în engleză). O pagină care citea direct din baza de date printr-un ORM, fărăfetch, nu îi dădea lui Next.js nimic de detectat, așa că era randată o dată și servită apoi la fel. - Rutinele pentru rutele
GETerau puse în cache dacă nu foloseau o funcție sau o configurare dinamică, după cum amintește anunțul versiunii RC (în engleză). - În browser, Router Cache păstra paginile vizitate 30 de secunde dacă erau dinamice și cinci minute dacă erau statice. Revalidarea datelor dintr-un Route Handler nu îl golea, așa că Router Cache continua să servească vechiul conținut până la un hard refresh sau până la expirarea intervalului (documentația de cache, în engleză).
Fiecare alegere se poate apăra separat. Împreună produc o eroare pe care mediul de dezvoltare o ascunde. Aceeași documentație precizează că, în development, datele din fetch sunt refolosite pentru Hot Module Replacement, iar opțiunile de cache sunt ignorate la un hard refresh. Programatorul reîncarcă pagina, vede date proaspete și trece mai departe. Full Route Cache apare abia după next build. Primul care vede pagina învechită e un utilizator din producție, iar ce raportează („am modificat și nu s-a schimbat nimic”) pare o eroare a funcționalității, nu a cache-ului.
Ce s-a schimbat și ce a recunoscut echipa mai târziu
Versiunea RC a schimbat trei valori implicite. Notele de lansare (în engleză) dau titlurile a două dintre pull requesturi fără ocolișuri: „[Breaking] Disable automatic fetch caching” și „[Breaking] disable client router cache for page segments”. Next.js 15 stabil (în engleză) a păstrat schimbările pe 21 octombrie 2024 și le-a trecut între modificările incompatibile. fetch nu mai folosește implicit force-cache. Rutinele pentru rutele GET nu mai sunt puse în cache. Router Cache are un staleTime de 0 pentru segmentele de pagină, deci navigarea obișnuită nu mai refolosește implicit acele segmente de pagină. Cache-ul de pe server și paginile statice pot conține totuși date vechi.
Trei zile mai târziu, Sebastian Markbåge a spus în articolul Our Journey with Caching (în engleză, 24 octombrie 2024) ce notele de lansare doar lăsau să se înțeleagă: experiența programatorilor a avut de suferit din cauza valorilor implicite și a controalelor de cache oferite. A numit și cauzele: fetch pus implicit în cache, prea puțin control asupra accesului la baza de date fără fetch și un șir de configurări pe segment (dynamic, fetchCache, revalidate), adăugate ca soluții de avarie. Modelul experimental pe care îl descria vă cere să decideți, pentru fiecare dată, dacă e pusă în cache sau e citită la fiecare cerere. În cuvintele lui: „Nothing is cached by default. No more hidden caches.” („Nimic nu e pus implicit în cache. Gata cu cache-urile ascunse.”)
Modelul a devenit Cache Components în Next.js 16 (în engleză, 21 octombrie 2025), construit în jurul directivei "use cache" și activat dintr-o opțiune de configurare. Anunțul îl pune în contrast cu cache-ul implicit din versiunile anterioare ale App Router și spune că, în Cache Components, cache-ul e în întregime opțional. Două versiuni majore la rând au mers în aceeași direcție, deci chiar cei care întrețin framework-ul le-au dat dreptate criticilor.
Cel mai bun argument pentru vechea regulă
Vechea regulă merită judecată corect. Pe majoritatea site-urilor, paginile sunt citite mult mai des decât se schimbă. Cache-ul implicit îi dă fiecărei echipe încărcarea rapidă a unui site static fără să-i ceară să înțeleagă cache-ul și ține jos încărcarea bazei de date și factura de hosting. Fără cache implicit, costul se mută pe fiecare cerere, iar echipele care nu se gândesc niciodată la cache îl plătesc în latență și în resurse de calcul. Markbåge recunoaște compromisul: mutarea citirii datelor pe server a însemnat sacrificarea încărcării inițiale foarte rapide din modelul Jamstack.
Argumentul are dreptate în privința costului, dar greșește în privința celui care trebuie să poarte riscul. O pagină lentă și corectă apare în monitorizare, iar cineva o repară adăugând un cache cu bună știință. O pagină rapidă și greșită apare ca reclamație, ca tichet la suport sau ca o comandă plasată la un preț vechi, iar cineva trebuie să caute cauza înapoi, prin patru niveluri de cache. Datele vechi sunt o eroare de corectitudine. Când o valoare implicită greșește, ar trebui să greșească într-un fel care se vede.
Unde concluzia nu se aplică
Schimbarea nu a făcut din Next.js 15 un framework fără cache, iar echipele care au făcut upgrade așteptând asta au avut surprize. Documentația fetch din Next.js 15 numește noua valoare implicită „auto no cache”: în development, Next.js citește resursa de la server la fiecare cerere, dar la next build o citește o singură dată, pentru că ruta e prerandată static (documentația fetch, v15, în engleză). O rută fără API-uri dinamice e construită tot o dată și servită ca atare. Problema pe care mediul de dezvoltare o ascunde rămâne, mai îngustă, pentru rutele statice. Anunțul RC enumera și ce rămâne în cache: layouturile comune nu sunt recitite la navigare, navigarea înapoi și înainte se face din cache, iar loading.js se păstrează cinci minute.
Pentru unele aplicații, teza contează mai puțin. Un site de prezentare, o documentație sau un blog care se reconstruiește la fiecare modificare de conținut era bine servit de vechea regulă, pentru că acolo randarea statică e chiar scopul. Argumentul privește aplicațiile ale căror date se schimbă între două deployuri: comenzi, stocuri, rezervări, pagini de cont, ecrane de administrare.
Cum verificați o aplicație Next.js 14 moștenită
Majoritatea aplicațiilor Next.js 14 încă în funcțiune au fost scrise cu vechile valori implicite, adesea de cineva care între timp a plecat. Dacă preluați o astfel de aplicație, aceste verificări ajută la găsirea riscurilor de date vechi înaintea utilizatorilor.
- Reproduceți producția, nu mediul de dezvoltare. Rulați local
next buildșinext startcu date apropiate de cele reale. Modificați o înregistrare și reîncărcați pagina. Cunext devnu veți vedea problema. - Faceți lista tuturor apelurilor
fetchde pe server fără opțiuneacachesaunext.revalidateexplicită. Sunt candidați pentru verificare, nu dovezi de cache. Referința v14 dăforce-cacheca valoare implicită dacă nu s-a folosit o funcție dinamică precumcookies(); setările efectivedynamicșifetchCachepot schimba comportamentul. Verificați ordinea apelurilor, configurația moștenită și contextul cererii. - Căutați paginile care citesc direct dintr-o bază de date sau dintr-un SDK și nu folosesc nicio funcție dinamică. Cu setările statice implicite, acestea pot fi randate la build; verificați configurația efectivă a segmentului. Decideți pentru fiecare dacă asta e intenția.
- Verificați fiecare handler pentru o rută
GET: are o funcție dinamică sauexport const dynamic? - Urmăriți fiecare modificare de date. Dacă modificarea afectează citiri din cache, identificați apelul necesar
revalidatePathsaurevalidateTagși eventuala reîmprospătare din client. Revalidarea dintr-un Route Handler lasă Router Cache din browser învechit 30 de secunde pe paginile dinamice și cinci minute pe cele statice.
Soluția e ca fiecare decizie să se vadă în cod. În Next.js 14, opțiunile arată așa:
Pentru o rută care afișează comenzi și catalogul, păstrați modul implicit dynamic = 'auto' și verificați setările fetchCache moștenite. Dați comenzilor opțiunea explicită cache: 'no-store'; astfel ruta nu intră în Full Route Cache. Pentru catalog, folosiți explicit cache: 'force-cache', cu next.revalidate: 300 și eticheta catalogue. Ghidul v14 permite citiri cu și fără cache într-o rută randată dinamic. Nu adăugați dynamic = 'force-dynamic': dezactivează și Data Cache, anulând politica pentru catalog. Aceasta este o explicație de configurare verificată în documentația versiunii, nu un exemplu executat; verificați ruta concretă în modul de producție înainte să vă bazați pe ea.
În această configurație, intenția este citirea comenzilor la fiecare cerere și revalidarea catalogului după cinci minute. Comportamentul trebuie verificat pentru ruta și versiunea instalate.
Decizia de upgrade vine după această verificare. Trecerea la 15 sau 16 schimbă valorile implicite, dar nu vă spune pe care dintre vechile citiri din cache se baza afacerea. Un upgrade fără verificare înlocuiește un set de surprize cu altul.
Principiul dincolo de Next.js
Lecția se aplică oricărui cache de care depinde o afacere: un CDN în fața unui API, un nivel Redis, un raport precalculat, datele offline ale unei aplicații mobile. Fiecare ar trebui să aibă un nume, un responsabil, o durată de viață declarată și un eveniment cunoscut care îl golește. Un cache pe care nu l-a ales nimeni e un cache la care nu se gândește nimeni când un client spune că cifrele sunt greșite. Next.js a avut nevoie de două versiuni majore ca să facă din asta regula implicită. O aplicație pe care o administrați poate ajunge la acest standard mai repede, iar locul acestei munci e mentenanța de rutină.
Surse
- Next.js 15 RC (în engleză), blogul Next.js, 23 mai 2024
- v15.0.0-rc.0, note de lansare (în engleză), vercel/next.js pe GitHub, 23 mai 2024
- Caching in Next.js, documentația v14 (în engleză), Next.js
- Server Components, strategii de randare, documentația v14 (în engleză), Next.js, actualizată pe 3 aprilie 2024
- Next.js 15 (în engleză), blogul Next.js, 21 octombrie 2024
- Our Journey with Caching (în engleză), Sebastian Markbåge, blogul Next.js, 24 octombrie 2024
- Referința API
fetch, documentația v15 (în engleză), Next.js - Next.js 16 (în engleză), blogul Next.js, 21 octombrie 2025
Paul