IlmHamroh
JavaScript Full-stack/23-qism. Next.js19/43-dars19 daqiqa
Mundarija (33)

Next.js keshini yangilash: revalidateTag, updateTag, revalidatePath va refresh

Qisqacha: "use cache" natijasi muddati tugaguncha saqlanadi. Muddatni kutmasdan yangilash uchun Next 16 da to'rtta vosita bor. Birinchisi — teg bo'yicha revalidateTag(teg, "max"): keyingi so'rov eski qiymatni darhol oladi, yangisi fonda olinadi. Xuddi shu funksiya { expire: 0 } bilan chaqirilsa, keyingi so'rov yangi qiymatni kutadi. Ikkinchisi — updateTag(teg), faqat Server Action ichida: foydalanuvchi o'z o'zgarishini darhol ko'radi. Uchinchisi — yo'l bo'yicha revalidatePath("/blog"). To'rtinchisi — refresh(): keshga tegmay, brauzerdagi sahifani serverdan qayta oladi. Eski loyihalarda buning o'rnida fetch ning next: { revalidate, tags } sozlamasi va unstable_cache uchraydi.

Bu darsda

  • revalidateTag ning ikki profilini ("max" va { expire: 0 }) haqiqiy o'lchov bilan solishtirasiz: 10 ms va 972 ms.
  • updateTag nega faqat Server Action'da ishlashini haqiqiy xato bilan ko'rasiz.
  • revalidatePath, refresh() va brauzerdagi router keshi qachon kerakligini ajratasiz.
  • Kanondagi /api/qayta-yangila marshrutini o'qiysiz: sir, timingSafeEqual, 404/401/405.
  • Next 14/15 kodidagi fetch kesh sozlamalari va unstable_cache ni o'qiy olasiz.
  • portfolio holati 19 ni commit qilasiz.

Oldin bilishingiz kerak: Cache Components I: "use cache" va PPR, Navigatsiya: Link, useRouter, Serverda ma'lumot olish, fetch bilan ma'lumot yuborish.

1. Nega bu kerak?

Oldingi darsda GitHub profili "use cache" + cacheLife("hours") bilan keshlandi. Sahifa tez, limit muammosi yo'q. Lekin yangi muammo paydo bo'ldi: siz GitHub'ga yangi loyiha qo'ydingiz va portfolio'da darhol ko'rmoqchisiz. Kesh esa bir soat eski ma'lumotni beradi.

Bozordagi narx taxtasini tasavvur qiling. Sotuvchi narxni ertalab yozadi va kun bo'yi o'zgartirmaydi — har xaridor uchun ulgurji bozorga qo'ng'iroq qilmaydi. Lekin ulgurji narx keskin o'zgarsa, kimdir unga "taxtani yangila" deb aytishi kerak. Keshni yangilash vositalari — o'sha "yangila" degan signal.

Muddatga asoslangan yangilash (cacheLife) — "har soatda". Talab bo'yicha yangilash (on-demand revalidation) — "hozir, chunki o'zgardi". Ikkalasi birga ishlaydi.

2. Teg bo'yicha yangilash

2.1 Teg va revalidateTag

Kanon keshlangan funksiyaga yorliq qo'ygan edi:

ts
export async function githubProfil(login: string) {
  "use cache";
  cacheTag("github");
  // …
}

Endi istalgan server kodida bitta chaqiruv shu yorliqli hamma kesh yozuvlarini eskirgan deb belgilaydi:

ts
import { revalidateTag } from "next/cache";

revalidateTag("github", "max");

Bitta funksiyaga bir nechta teg qo'yish mumkin (cacheTag("github", "haqimda")), bitta tegni bir nechta funksiya ishlatishi mumkin. Teg — oddiy satr, katta-kichik harf farqlanadi, 256 belgidan oshmasin.

revalidateTag qayerda chaqiriladi? Next.js hujjati bo'yicha — Server Function'larda (Server Action — keyingi darslar) va Route Handler'larda. Client Component'da emas, proxy.ts da ham emas.

2.2 Ikkinchi argument: qancha vaqt eskisi beriladi

Next 16 da revalidateTag ning ikkinchi argumenti majburiy. U "eskirgan qiymatni yana qancha vaqt berish mumkin" degan savolga javob:

Chaqiruv Keyingi so'rov Qachon
revalidateTag(t, "max") Eskisini darhol oladi, yangisi fonda Hujjat tavsiyasi; ko'p hollarda
revalidateTag(t, { expire: 0 }) Yangisini kutadi Eski ma'lumot ko'rinmasligi shart
revalidateTag(t) (bitta argument) { expire: 0 } kabi Eskirgan (deprecated), yozmang

"max" — Cache Components I dagi stale-while-revalidate naqshi, faqat signal bilan boshlanadi. Muhim detal: yangilanish revalidateTag chaqirilgan paytda emas, keyingi so'rov kelganda boshlanadi. Hech kim sahifani ochmasa — GitHub'ga so'rov ham ketmaydi.

2.3 Biz o'lchadik: uchta usul yonma-yon

Kanon /api/qayta-yangila marshrutida { expire: 0 } ni tanlagan. Farqni ko'rish uchun biz kanon nusxasida (holat 24) marshrutni vaqtincha o'zgartirdik: ?usul= bilan uch xil chaqiruvni tanlaydigan qildik. GitHub o'rnida har javobni 800 ms kechiktiradigan lokal soxta server qo'ydik. U har profil so'rovida public_repos ni bittaga oshiradi — sahifadagi repo= raqami yangi ma'lumot kelganini ko'rsatadi. Keyin next build + next start:

text
  isitish: 200, 60 ms, HIT, repo=1
  isitish: 200, 5 ms, HIT, repo=1
POST usul=expire0: 200 {"yangilandi":"github","usul":"expire0"}
  1-so'rov: 200, 972 ms, MISS, repo=2
  2-so'rov: 200, 9 ms, HIT, repo=2
POST usul=max: 200 {"yangilandi":"github","usul":"max"}
  1-so'rov: 200, 10 ms, STALE, repo=2
  2-so'rov: 200, 5 ms, HIT, repo=3
POST usul=update: 500 
  1-so'rov: 200, 5 ms, HIT, repo=3
  2-so'rov: 200, 5 ms, HIT, repo=3

Uchinchi ustun — x-nextjs-cache javob sarlavhasi. Qatorma-qator:

  • expire0 dan keyin birinchi so'rov 972 ms kutdi (MISS): kesh o'chirilgan, server GitHub'ni kutdi. Lekin darhol yangi ma'lumot — repo=2.
  • max dan keyin birinchi so'rov 10 ms (STALE): eski repo=2 berildi, server fonda GitHub'ga bordi. Ikkinchi so'rov — yangi repo=3.
  • update — 500 xato. Sababi keyingi bo'limda.
Yangilashdan keyingi birinchi so'rov vaqti
  • revalidateTag expire: 0MISS, yangi ma'lumot972 ms
  • revalidateTag "max"STALE, eski ma'lumot10 ms

Manba: Biz ishga tushirdik: portfolio v2 holat 24 nusxasi, next start, GitHub o'rnida 800 ms soxta server, 2026-10-21

Ustunlar tanlovning narxini ko'rsatadi: tezlik yoki birinchi foydalanuvchiga yangi ma'lumot. Kanonning o'z o'lchovi (haqiqiy GitHub bilan, natijalar/19-qayta-yangila.txt) ham expire: 0 uchun shunday: yangilashdan keyingi so'rov 854 ms MISS, keyin 17 ms HIT.

Kanon nega expire: 0 ni tanladi? Bu marshrutni siz o'zingiz chaqirasiz — "GitHub'ga yangi loyiha qo'ydim, saytda ko'rmoqchiman". Keyingi ochgan odam (ehtimol o'zingiz) yangi ro'yxatni ko'rishi kerak, bir soniya kutish esa muammo emas. Ko'p o'quvchili yangiliklar saytida esa "max" to'g'riroq: hech kim sekin sahifa ko'rmaydi.

Tekshirib ko'ring: revalidateTag("github", "max") chaqirildi, keyin bir soat hech kim /haqimda ni ochmadi. GitHub'ga necha marta so'rov ketdi?

Javob

Nol. Yangilanish so'rov bilan boshlanadi, revalidateTag chaqiruvi bilan emas. Bir soatdan keyin birinchi ochgan odam eski qiymatni oladi va o'sha paytda fonda yangi so'rov ketadi.

2.4 Natija oynasida: teg bo'yicha yangilash modeli

O'lchovdagi xulqni oddiy JS modelida takrorlaymiz. Bu Next.js kodi emas — faqat qoidalari: teg, "max" (eskisini ber, fonda yangila) va { expire: 0 } (o'chir, kutib ol):

js
let calls = 0;
const store = new Map();

async function load(login) {
  calls++;
  return `${login}: ${calls}-versiya`;
}

async function githubProfile(login) {
  const entry = store.get(login);
  if (entry && !entry.stale) {
    console.log("HIT");
    return entry.value;
  }
  if (entry) {
    console.log("STALE: eskisi, fonda yangilanadi");
    load(login).then((v) =>
      store.set(login, { value: v, tags: ["github"] }),
    );
    return entry.value;
  }
  console.log("MISS: kutamiz");
  const value = await load(login);
  store.set(login, { value, tags: ["github"] });
  return value;
}

function revalidateTag(tag, profile) {
  for (const [key, entry] of store) {
    if (!entry.tags.includes(tag)) continue;
    if (profile === "max") entry.stale = true;
    else store.delete(key); // { expire: 0 }
  }
}

console.log(await githubProfile("octocat"));
console.log(await githubProfile("octocat"));
revalidateTag("github", { expire: 0 });
console.log(await githubProfile("octocat"));
revalidateTag("github", "max");
console.log(await githubProfile("octocat"));
console.log(await githubProfile("octocat"));

Konsolda:

text
MISS: kutamiz
octocat: 1-versiya
HIT
octocat: 1-versiya
MISS: kutamiz
octocat: 2-versiya
STALE: eskisi, fonda yangilanadi
octocat: 2-versiya
HIT
octocat: 3-versiya

Model haqiqiy o'lchov bilan bir xil manzarani beradi: expire: 0 dan keyin MISS va yangi versiya, "max" dan keyin STALE va eski versiya, keyingi so'rovda yangi.

3. updateTag: o'zgarishingizni darhol ko'rish

3.1 Read-your-own-writes

Faraz qiling, foydalanuvchi blogga izoh yozdi va «Yuborish» ni bosdi. Sahifa qayta chizilganda u o'z izohini ko'rishi shart — "eskisi fonda yangilanadi" bu yerda yaramaydi. Bu naqsh read-your-own-writes (yozganingni o'qi) deyiladi.

Shu holat uchun updateTag:

ts
"use server";
import { updateTag } from "next/cache";

export async function izohQosh(forma: FormData) {
  await izohniSaqla(forma); // bazaga — 26-qismdan
  // kesh darhol eskirdi, sahifa yangisini kutadi
  updateTag("izohlar");
}

updateTag — revalidateTag(t, { expire: 0 }) ga o'xshaydi, lekin u Server Action ichida ishlaydi va Next.js shu action javobida yangilangan sahifani ham qaytaradi. Server Action'larni Server Actions I darsida yozamiz.

3.2 Biz topdik: Route Handler'da — 500

Biz o'lchovdagi ?usul=update da updateTag("github") ni Route Handler ichida chaqirdik. Build xatosiz o'tdi, lekin so'rov paytida server logida:

text
⨯ Error: updateTag can only be called from within a Server Action. To invalidate cache tags in Route Handlers or other contexts, use revalidateTag instead. See more info here: https://nextjs.org/docs/app/api-reference/functions/updateTag

Tarjimasi: "updateTag faqat Server Action ichidan chaqirilishi mumkin. Route Handler yoki boshqa joyda kesh teglarini yangilash uchun revalidateTag ishlating". Javob — 500, kesh o'zgarmadi (repo=3 qoldi). Xato faqat ishga tushganda chiqadi — TypeScript ham, build ham ogohlantirmaydi. Kanon izohi buni oldindan bilgan: "Tashqaridan (webhook, cron) chaqirilganda updateTag yo'q".

4. Yo'l bo'yicha va router bo'yicha yangilash

4.1 revalidatePath

Teg bo'lmasa yoki "shu sahifadagi hamma narsa" kerak bo'lsa — yo'l bo'yicha:

ts
import { revalidatePath } from "next/cache";

revalidatePath("/haqimda"); // bitta sahifa
// shu shablondagi hamma sahifa
revalidatePath("/blog/[slug]", "page");
// layout va ichidagi hamma sahifa
revalidatePath("/loyihalar", "layout");

Dinamik segmentli yo'lda ikkinchi argument ("page" yoki "layout") majburiy. Teg aniqroq: u qaysi ma'lumot o'zgarganini aytadi va shu ma'lumotni ishlatgan hamma sahifani topadi. Yo'l esa "qaysi sahifa" deydi va ichidagi har bir kesh yozuvini yangilaydi. Hujjat ikkalasini birga ishlatish mumkinligini aytadi.

4.2 Brauzerdagi router keshi

Kesh faqat serverda emas. Next.js'ning client router'i ham ko'rilgan va oldindan yuklangan sahifalarni brauzer xotirasida saqlaydi. Uning muddati — cacheLife dagi stale (tayyor profillarda 5 daqiqa). Shu vaqt ichida <Link> bilan qaytsangiz, router serverga so'rov yubormaydi.

Kesh Qayerda Kim tozalaydi
"use cache" yozuvlari Server xotirasi revalidateTag, revalidatePath, updateTag, muddat
Statik HTML (○) Server / CDN Xuddi shular (sahifa qayta yaratiladi)
Router keshi Brauzer xotirasi stale muddati, router.refresh(), Server Action

router.refresh() ni Navigatsiya darsida ko'rgansiz — client komponentdan joriy sahifani serverdan qayta so'raydi. Uning server tomondagi juftligi — refresh():

ts
"use server";
import { refresh } from "next/cache";

export async function belgila() {
  // … keshlanmagan ma'lumotni o'zgartirdik
  refresh(); // brauzerdagi joriy sahifa serverdan qayta olinadi
}

refresh() keshga tegmaydi. U faqat "brauzerdagi router, sahifani qaytadan so'ra" deydi. Ma'lumot keshlanmagan bo'lsa — shu yetarli. Keshlangan bo'lsa — avval teg yoki yo'lni yangilash kerak. refresh() ham faqat Server Action ichida ishlaydi (hujjat: "can only be called from within Server Actions").

flowchart TB
  O["Ma'lumot o'zgardi"] --> K{"Kim chaqiryapti?"}
  K -- "Server Action, o'z o'zgarishi" --> U["updateTag(teg)"]
  K -- "Webhook, cron, admin" --> R{"Eskisi ko'rinsa bo'ladimi?"}
  R -- "ha" --> M["revalidateTag(teg, 'max')"]
  R -- "yo'q" --> E["revalidateTag(teg, { expire: 0 })"]
  K -- "keshlanmagan, faqat ekran" --> F["refresh()"]

Diagrammadan qaror qabul qilish oson: avval "kim chaqiryapti?", keyin "eskisi ko'rinsa bo'ladimi?".

Tekshirib ko'ring: cron har kecha 03:00 da valyuta kurslarini yangilaydi va Route Handler'ni chaqiradi. Qaysi funksiya?

Javob

revalidateTag("kurslar", "max"). Route Handler'da updateTag ishlamaydi. Kechasi bir-ikki o'quvchiga bir necha soniya eski kurs ko'rinsa ham, hech kim sekin sahifa ko'rmaydi. To'lov hisob-kitobida aniq kurs kerak bo'lsa — { expire: 0 }.

5. Kanon: /api/qayta-yangila

5.1 Marshrut fayli

Keshni tashqaridan yangilash uchun kanon kichik API marshrut qo'shdi. app/api/qayta-yangila/route.ts — bu Route Handler: page.tsx o'rnida route.ts va HTML o'rnida JSON qaytaradigan funksiya. To'liq — Route Handlers darsida, bugun faqat shu misol:

ts
import { revalidateTag } from "next/cache";
import { timingSafeEqual } from "node:crypto";

// GitHub keshini qo'lda yangilash (N-19):
// `curl -X POST -H "x-siri: …" /api/qayta-yangila`.
// Sir — server env'da (NEXT_PUBLIC_ emas, N-39).
// Berilmagan bo'lsa marshrut o'chiq.
function sirTogri(kelgan: string | null): boolean {
  const siri = process.env.QAYTA_YANGILASH_SIRI;
  if (!siri || !kelgan) return false;
  const a = Buffer.from(kelgan);
  const b = Buffer.from(siri);
  return a.length === b.length && timingSafeEqual(a, b);
}

export async function POST(request: Request) {
  if (!process.env.QAYTA_YANGILASH_SIRI)
    return new Response(null, { status: 404 });
  if (!sirTogri(request.headers.get("x-siri")))
    return Response.json({ xato: "ruxsat yo'q" }, { status: 401 });
  // Tashqaridan (webhook, cron) chaqirilganda updateTag yo'q —
  // expire: 0 darhol eskirtiradi (hujjat: revalidateTag)
  revalidateTag("github", { expire: 0 });
  return Response.json({
    yangilandi: "github",
    vaqt: new Date().toISOString(),
  });
}

Kanon izohlari qisqa qatorlarga bo'lindi. Uch qatlamli himoya:

Qatlam Nima qiladi
Env yo'q → 404 Sir sozlanmagan serverda marshrut umuman "yo'q"
Sir noto'g'ri → 401 x-siri sarlavhasi server siriga teng bo'lishi shart
Faqat POST GET → 405 (Next.js o'zi qaytaradi)

Nega sir sarlavhada, URL'da emas? URL server loglariga, brauzer tarixiga va proksi loglariga yoziladi. Sarlavha odatda yozilmaydi.

Build jadvalida: ƒ /api/qayta-yangila — to'liq dinamik (POST har doim so'rov paytida).

5.2 timingSafeEqual nega kerak

Oddiy kelgan === siri satrlarni chapdan harfma-harf solishtiradi va birinchi farqda to'xtaydi. Birinchi harfi to'g'ri sir biroz sekinroq rad etiladi. Hujumchi minglab so'rov yuborib, javob vaqtidan sirni harfma-harf topishga urinishi mumkin — bu timing attack (vaqt hujumi). timingSafeEqual esa har doim butun buferni solishtiradi — vaqt sirga bog'liq emas.

Uning bitta talabi: ikkala bufer uzunligi teng bo'lishi shart, aks holda xato tashlaydi. Shuning uchun kanon avval a.length === b.length ni tekshiradi. Node'da sinab ko'ring:

js
import { timingSafeEqual } from "node:crypto";

function secretMatches(given, secret) {
  const a = Buffer.from(given);
  const b = Buffer.from(secret);
  return a.length === b.length && timingSafeEqual(a, b);
}

console.log(secretMatches("sinov-siri", "sinov-siri")); // true
console.log(secretMatches("sinov-sirX", "sinov-siri")); // false
console.log(secretMatches("qisqa", "sinov-siri")); // false

try {
  timingSafeEqual(Buffer.from("qisqa"), Buffer.from("sinov-siri"));
} catch (error) {
  console.log(error.code); // ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH
}

Oxirgi qator: uzunlik tekshiruvisiz timingSafeEqual tashlaydigan xato kodi. node:crypto — Node.js moduli, brauzerda yo'q; uni 24-qismda (crypto: hash va HMAC) chuqur o'rganamiz. Saytda bu blok uchun «Ishga tushir» yo'q.

5.3 Oqim: kanon o'lchovi

Kanon to'liq oqimni tekshirdi (natijalar/19-qayta-yangila.txt):

text
1. build'dagi sahifa: 200, 75 ms, x-nextjs-cache=HIT, cache-control=s-maxage=3600, stale-while-revalidate=82800, repo=8
2. yana: 200, 16 ms, x-nextjs-cache=HIT, cache-control=s-maxage=3600, stale-while-revalidate=82800, repo=8
POST sirsiz: 401 {"xato":"ruxsat yo'q"}
POST noto'g'ri sir: 401 {"xato":"ruxsat yo'q"}
POST to'g'ri sir: 200 {"yangilandi":"github","vaqt":"2026-10-20T20:01:47.302Z"}
3. yangilashdan keyin 1-so'rov: 200, 854 ms, x-nextjs-cache=MISS, cache-control=s-maxage=3600, stale-while-revalidate=82800, repo=8
4. keyin: 200, 17 ms, x-nextjs-cache=HIT, cache-control=s-maxage=3600, stale-while-revalidate=82800, repo=8
GET /api/qayta-yangila: 405

repo=8 o'zgarmadi — haqiqiy octocat hisobida yangi repo yo'q edi. Lekin MISS va 854 ms GitHub'ga haqiqatan qayta borilganini ko'rsatadi.

Terminaldan chaqirish:

bash
curl -X POST -H "x-siri: SIZNING_SIRINGIZ" \
  http://localhost:3000/api/qayta-yangila

5.4 .env.example va biz bilmagan tuzoq

Sir kodga yozilmaydi — u .env.local faylida turadi. Boshqa dasturchi (yoki kelajakdagi siz) qaysi o'zgaruvchilar kerakligini bilishi uchun namuna fayl — .env.example — git'ga qo'shiladi:

bash
# Nusxa oling: cp .env.example .env.local (.env* git'ga tushmaydi)
# /api/qayta-yangila uchun sir (ixtiyoriy; bo'lmasa marshrut 404).
# Yaratish: node -e "console.log(crypto.randomUUID())"
QAYTA_YANGILASH_SIRI=

Kanon topilmasi: create-next-app skeletidagi .gitignore da .env* qatori bor — u .env.example ni ham yashiradi. Fayl git'ga tushmay qoladi. Tuzatish — .gitignore ga bitta qator:

diff
 .env*
+!.env.example

! — "oldingi qoidadan istisno" (.gitignore qoidalari). Env va sirlar haqida batafsil — Env o'zgaruvchilar va sirlar.

6. Hujumchi nigohi

/api/qayta-yangila — internetga ochiq manzil. Hujumchi nima qilishi mumkin?

Hujum Kanon nima qiladi
Sirsiz POST yuborib keshni tinimsiz tozalash → har so'rov GitHub'ga → limit tugaydi 401, kesh o'zgarmaydi
Sirni taxmin qilish (brute force) randomUUID — 122 bit tasodif, taxmin qilib bo'lmaydi
Javob vaqtidan sirni topish timingSafeEqual
Sirni koddan yoki brauzer to'plamidan o'qish Sir faqat server env'da, NEXT_PUBLIC_ siz
Sir sozlanmagan serverda marshrutni topish 404 — marshrut "yo'q"

Kanonda yo'q, lekin real loyihada qo'shiladigan narsa — tezlik cheklovi (rate limit): to'g'ri sir bilan ham daqiqasiga bir necha marta. Uni Server Actions xavfsizligi darsida yozamiz.

7. Eski model: legacy kodni o'qish

Next 14/15 loyihalarida kesh fetch ning o'zida sozlanardi. Bu kodni uchratasiz:

ts
// Next 14/15 (cacheComponents yoqilmagan) — eski model
const javob = await fetch("https://api.github.com/users/octocat", {
  next: { revalidate: 3600, tags: ["github"] },
});

// Hech qachon keshlama
await fetch(url, { cache: "no-store" });

// Doim keshla (Next 14 da bu sukut edi)
await fetch(url, { cache: "force-cache" });

fetch bo'lmagan funksiya (baza so'rovi) uchun — unstable_cache:

ts
import { unstable_cache } from "next/cache";

const loyihalar = unstable_cache(
  async () => db.loyiha.findMany(),
  ["loyihalar"], // kesh kaliti qismi
  { revalidate: 3600, tags: ["loyihalar"] },
);

Yangi modelga tarjimasi:

Eski Yangi (Cache Components)
next: { revalidate: 3600 } "use cache" + cacheLife("hours")
next: { tags: ["github"] } cacheTag("github")
cache: "no-store" Hech narsa (sukut — keshlanmaydi)
cache: "force-cache" "use cache"
unstable_cache(fn, kalit, opts) "use cache" funksiyada (kalit — argumentlar)
revalidateTag("github") revalidateTag("github", "max") yoki updateTag

Bitta istisno: hujjat bo'yicha "use cache" yozuvlari yangi deploy'da tozalanadi (kalitda build ID). Deploy'dan deploy'ga o'tishi kerak bo'lgan kesh uchun hujjat hali ham unstable_cache yoki fetch keshini tilga oladi. Kanonga bu kerak bo'lmadi.

Tarix uchun: Next 14 da fetch sukut bo'yicha keshlanardi va ko'p dasturchi "nega ma'lumot yangilanmayapti?" deb qiynalardi. Next 15 sukutni "keshlanmaydi" ga o'zgartirdi, Next 16 esa keshni butunlay "use cache" ga ko'chirdi.

8. Portfolio qadami: qayta yangilash va commit

generateStaticParams, Cache Components I va shu dars — bitta holat (19). Bugun oxirgi qismi va commit.

8.1 Fayllar

Fayl O'zgarish
src/app/api/qayta-yangila/route.ts Yangi: POST, x-siri, timingSafeEqual, revalidateTag("github", { expire: 0 })
.env.example Yangi: QAYTA_YANGILASH_SIRI=
.gitignore !.env.example
src/malumot/github.ts (oldingi dars) "use cache", teg, shartli muddat

8.2 Tekshiruv

  1. Sir yarating: node -e "console.log(crypto.randomUUID())" va .env.local ga QAYTA_YANGILASH_SIRI=… deb yozing.
  2. npm run build && npm run start.
  3. Sirsiz curl -X POST …/api/qayta-yangila → 401; sir bilan → 200 {"yangilandi":"github",…}; curl …/api/qayta-yangila (GET) → 405.
  4. git status da .env.example ko'rinadi, .env.local esa yo'q.

Kanon natijasi: ƒ /api/qayta-yangila, ○ /haqimda 1h 1d, JS o'zgarmadi, axe 0, hydration 0.

8.3 Commit

bash
git add -A
git commit

Commit (kanon): Kesh: GitHub profili "use cache" (soat/daqiqa), cacheTag; /api/qayta-yangila (sir bilan).

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
updateTag can only be called from within a Server Action updateTag Route Handler'da revalidateTag(teg, profil)
TS xatosi revalidateTag("x") da Ikkinchi argument yo'q (eskirgan shakl) "max" yoki { expire: 0 }
Yangilash chaqirildi, sahifa eski "max" — birinchi so'rov eskisini oladi Yana bir so'rov yoki { expire: 0 }
Teg bo'yicha yangilash ta'sir qilmaydi cacheTag yozilmagan yoki harf farqi Bir xil teg satri, bir joyda konstanta
revalidatePath("/blog/[slug]") ishlamaydi Dinamik yo'lda type yo'q revalidatePath(…, "page")
.env.example git'da yo'q .gitignore dagi .env* !.env.example
timingSafeEqual xato tashladi Buferlar uzunligi har xil Avval uzunlikni solishtiring
Eski kodda revalidate ishlamaydi cacheComponents: true yoqilgan Yangi modelga ko'chiring

10. Mashqlar

Mashqlar kurs/mashqlar/23/19-revalidatsiya/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.

1-mashq (oson): qaysi vosita?

  • Admin panelda narxni o'zgartirdi, o'zi darhol ko'rishi kerak (Server Action):
  • Telegram bot webhook'i yangi xabar keldi deydi (Route Handler), eski ro'yxat bir necha soniya ko'rinsa bo'ladi:
  • Keshlanmagan ma'lumot o'zgardi, joriy sahifani qayta chizish kerak (Server Action):
  • Teg yo'q, /narxlar sahifasining hammasi yangilansin:
Yechim
  • updateTag — read-your-own-writes, faqat Server Action.
  • revalidateTag — "max" profili bilan; Route Handler'da updateTag ishlamaydi.
  • refresh — keshga tegmaydi, router sahifani qayta oladi.
  • revalidatePath — yo'l bo'yicha.

2-mashq (o'rta): bir nechta teg

«Teg bo'yicha yangilash modeli» blokini kengaytiring: ikkita funksiya — githubProfile(login) (teglar ["github"]) va githubRepos(login) (teglar ["github", "repos"]). revalidateTag("repos", { expire: 0 }) dan keyin qaysi biri MISS, qaysi biri HIT bo'lishini ko'rsating. Ikkala funksiya bitta store ni ishlatsin, kalit — "profil:octocat" va "repos:octocat".

Yechim
js
let calls = 0;
const store = new Map();

async function cachedCall(key, tags) {
  const entry = store.get(key);
  if (entry) {
    console.log(`HIT ${key}`);
    return entry.value;
  }
  console.log(`MISS ${key}`);
  calls++;
  const value = `${key} #${calls}`;
  store.set(key, { value, tags });
  return value;
}

const githubProfile = (login) =>
  cachedCall(`profil:${login}`, ["github"]);
const githubRepos = (login) =>
  cachedCall(`repos:${login}`, ["github", "repos"]);

function revalidateTag(tag) {
  for (const [key, entry] of store) {
    if (entry.tags.includes(tag)) store.delete(key);
  }
}

await githubProfile("octocat");
await githubRepos("octocat");
revalidateTag("repos");
await githubProfile("octocat");
await githubRepos("octocat");
revalidateTag("github");
await githubProfile("octocat");
await githubRepos("octocat");
console.log("so'rovlar:", calls);

Konsolda:

text
MISS profil:octocat
MISS repos:octocat
HIT profil:octocat
MISS repos:octocat
MISS profil:octocat
MISS repos:octocat
so'rovlar: 5

"repos" tegi faqat repolarni tozaladi. "github" — ikkalasini, chunki ikkala yozuvda ham bu teg bor. Teglarni shunday qatlamlab qo'yish mumkin: umumiy (github) va aniq (repos).

3-mashq (qiyin): xavfsiz sir tekshiruvi

checkRequest(headers, env) funksiyasini yozing. U HTTP status qaytaradi: env.SECRET bo'lmasa — 404; headers["x-siri"] yo'q yoki noto'g'ri — 401; to'g'ri — 200. Solishtirish uzunlik tekshiruvi + timingSafeEqual bilan. To'rt holatni sinang.

Yechim
js
import { timingSafeEqual } from "node:crypto";

function checkRequest(headers, env) {
  if (!env.SECRET) return 404;
  const given = headers["x-siri"];
  if (!given) return 401;
  const a = Buffer.from(given);
  const b = Buffer.from(env.SECRET);
  if (a.length !== b.length || !timingSafeEqual(a, b)) return 401;
  return 200;
}

const env = { SECRET: "sinov-siri" };
console.log(checkRequest({}, {})); // 404
console.log(checkRequest({}, env)); // 401
console.log(checkRequest({ "x-siri": "qisqa" }, env)); // 401
console.log(checkRequest({ "x-siri": "sinov-siri" }, env)); // 200

Uzunlik tekshiruvi timingSafeEqual dan oldin: aks holda "qisqa" sir 401 o'rniga xato (500) berardi. Uzunlik farqi sirning uzunligini oshkor qiladi, lekin uzun tasodifiy sirda bu xavf emas.

4-mashq: Portfolio qadami

app/api/qayta-yangila/route.ts, .env.example va .gitignore dagi istisnoni qo'shing. «Tekshiruv» bo'limidagi to'rt qadamni bajaring. git status da .env.local yo'qligiga ishonch hosil qiling va commit qiling.

Yechim

Namuna — «Kanon: /api/qayta-yangila» bo'limidagi kod. Windows PowerShell'da curl o'rniga curl.exe yozing (PowerShell'da curl — boshqa buyruqning taxallusi). Natijalar kanondagidek: 401, 401, 200, 405. Commit xabari — kanondagi yoki o'zingizniki.

11. Real ishda

  • CMS webhook'lari (Contentful, Sanity, Strapi) — maqola chop etilganda Route Handler'ni chaqiradi, u revalidateTag qiladi. Bu Next.js saytlarining eng keng tarqalgan naqshi.
  • E-commerce — narx yoki qoldiq o'zgarganda faqat shu mahsulot tegi yangilanadi, butun sayt emas.
  • Admin panellar — updateTag bilan: admin o'zgarishini darhol ko'radi.
  • Intervyuda: "On-demand revalidation qanday ishlaydi?", "revalidateTag va revalidatePath farqi?", "Webhook'ni qanday himoya qilasiz?" — Next.js va umumiy backend savollari.

Xulosa

  • cacheTag yorliq qo'yadi; revalidateTag(teg, "max") — eskisi darhol, yangisi fonda; { expire: 0 } — keyingi so'rov kutadi (biz o'lchadik: 10 ms va 972 ms).
  • Yangilanish chaqiruv paytida emas, keyingi so'rovda boshlanadi.
  • updateTag va refresh() — faqat Server Action'da; Route Handler'da updateTag — 500.
  • revalidatePath yo'l bo'yicha; router keshi brauzerda stale muddati bilan yashaydi.
  • Kanon /api/qayta-yangila: env yo'q — 404, sir noto'g'ri — 401, GET — 405; timingSafeEqual va !.env.example.
  • Eski fetch sozlamalari va unstable_cache — Next 14/15 kodini o'qish uchun.

Keyingi dars: React cache(), request memoization va Data Access Layer — bitta so'rov ichidagi takroriy chaqiruvlar va ma'lumot qatlami.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Next.js keshini yangilash: revalidateTag, updateTag, revalidatePath va refresh — IlmHamroh