// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const secret = request.headers.get('x-webhook-secret');
if (secret !== process.env.CMS_WEBHOOK_SECRET) {
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
}
const body = await request.json();
const productId = body?.data?.id;
if (!productId) {
return NextResponse.json({ message: 'Missing product ID' }, { status: 400 });
}
// Invalidatează doar cache-ul asociat acestui produs
revalidateTag(`product-${productId}`);
return NextResponse.json({ revalidated: true, now: Date.now() });
}Am văzut prea multe proiecte în Next.js unde echipa a pus revalidate: 60 peste tot fără să înțeleagă ce se întâmplă în fundal. Am pățit-o și eu pe un e-commerce cu peste 12k de produse: ne trezeam cu invocări serverless enorme pe Vercel și cu prețuri vechi afișate utilizatorilor exact când schimbam promoțiile. Hai să trecem prin opțiuni și să vedem ce alegi în funcție de ce construiești.
1. SSG (Static Site Generation)
Util când datele se schimbă rar sau doar la deploy. Este opțiunea cea mai ieftină și mai rapidă, paginile fiind servite direct din CDN.
- Caz de utilizare: Landing pages, pagini de „Despre noi”, documentație tehnică.
- Trade-off: Dacă ai 5.000 de articole pe un blog, un build complet poate dura 10-15 minute. Când schimbi un typo în footer, trebuie să recompilezi tot.
2. ISR pe bază de timp (Time-based ISR)
Pui un revalidate = 3600 și Next.js generează pagina static, iar după o oră, la primul request, o regenerează în fundal.
- Caz de utilizare: Bloguri mari sau site-uri de știri unde nu e o tragedie dacă un articol modificat apare pentru unii utilizatori cu 10 minute întârziere.
- Trade-off: La un spike brusc de trafic pe o pagină expirată, primul user primește HTML-ul vechi, iar serverul muncește să-l facă pe cel nou. Dacă ai mii de pagini accesate rar, umpli cache-ul degeaba.
3. On-Demand Revalidation (cu Tags sau Paths)
Pentru mine, ăsta e „sweet spot-ul” în App Router. Generezi pagina static sau la primul request, iar când se schimbă ceva în CMS sau DB, trimiți un webhook care apelează revalidateTag() sau revalidatePath().
- Caz de utilizare: E-commerce (pagina de produs). Când managerul schimba prețul în admin, dăm trigger la webhook și cache-ul se invalidează instant doar pentru acel produs.
- Trade-off: E nevoie de puțin mai mult cod în backend/CMS pentru a gestiona webhook-urile. Dacă pică webhook-ul, rămâi cu date vechi până la următorul deploy.
4. SSR / Dynamic Rendering
Pagina se generează la fiecare request. Zero cache pe HTML.
- Caz de utilizare: Dashboard-ul utilizatorului, coșul de cumpărături, setările contului.
- Trade-off: TTFB mai mare decât la opțiunile statice și consum direct de resurse de compute la fiecare accesare.
Ghid rapid de decizie în 5 secunde
- Dashboard / Setări cont: SSR direct. Nu vrei cache pe date personale.
- Pagini de produs (E-commerce): On-demand revalidation prin
revalidateTag('product-id'). În felul ăsta ai TTFB de 20ms de pe CDN și update instant la preț. - Blog / Site de știri: ISR pe timp (
revalidate: 3600) sau On-Demand direct din CMS. - Docs / Landing page: SSG pur. Faci build doar la commit.
- Listing cu filtre multe (Căutare): Dynamic (SSR) cu
suspenseși streaming, altfel îți blochezi render-ul pe server.
Noi am trecut catalogul de la ISR la On-Demand Revalidation și am redus numărul de invocări pe serverless cu 42% în prima lună. Voi ce strategie folosiți cel mai des în App Router?