Caching — Request Memoization, Data Cache, Full Route Cache

~25 daq · Next.js · Performance va Optimizatsiya

Muammodan boshlaylik

Next.js bilan ishlaganlarning eng ko'p savollari:

  • "Bazada narxni o'zgartirdim, saytda eski narx turibdi!"
  • "Build dan keyin sahifa umuman yangilanmayapti"
  • "Har so'rovda bazaga murojaat — sayt sekin"
  • "fetch bir xil URL ga 5 marta chaqirildi, lekin so'rov bitta ketdi — nega?"

Javob — Next.js ning bir nechta kesh qatlamlari. Ularni tushunmasdan, xatti-harakat "sehrli" va kutilmagan ko'rinadi.


Hayotiy o'xshatma

Restoran oshxonasi. Bitta buyurtma ichida bir xil sous ikki taomga kerak — bir marta tayyorlanadi (Request Memoization). Tez-tez so'raladigan bulyon — katta qozonda kun bo'yi saqlanadi (Data Cache). Eng mashhur salatlar — oldindan tayyorlab vitrinaga qo'yilgan (Full Route Cache). Mijozning stolida — oldingi taomlar hali turibdi (Router Cache).


To'rt qatlam

Qatlam Nima Qayerda Qancha
Request Memoization Bir xil fetch natijasi Server, bitta render Bitta so'rov davomida
Data Cache fetch / unstable_cache natijalari Server So'rovlar va deploy lar orasida
Full Route Cache Tayyor HTML va RSC payload Server Build dan keyin, revalidate gacha
Router Cache Ko'rilgan sahifalar RSC payload i Brauzer Sessiya davomida

1. Request Memoization

async function getUser(id: string) {
  const res = await fetch(`https://api.dokon.uz/users/${id}`)
  return res.json()
}

// layout.tsx, page.tsx, Header.tsx — hammasi getUser('7') ni chaqiradi

Bitta render davomida bir xil URL va opsiyali fetch — bir marta bajariladi. Ma'lumotni "prop drilling" siz har bir komponentda bemalol so'rash mumkin.

fetch emas (Prisma, ORM) — React.cache bilan:

import { cache } from 'react'

export const getUser = cache(async (id: string) => prisma.user.findUnique({ where: { id } }))

2. Data Cache

// keshlanadi — revalidate gacha
await fetch('https://api.dokon.uz/kategoriyalar', { next: { revalidate: 3600 } })

// abadiy (qo'lda revalidate gacha)
await fetch('https://api.dokon.uz/sozlamalar', { cache: 'force-cache' })

// keshlanmaydi
await fetch('https://api.dokon.uz/narxlar', { cache: 'no-store' })

[!INFO] Next.js 15 da fetch standart holatda keshlanmaydi (no-store). Next.js 14 da aksincha — standart keshlanardi va bu ko'p chalkashlikka sabab bo'lgan. Endi keshlashni aniq so'rash kerak: cache: 'force-cache' yoki next: { revalidate }.

fetch emas — unstable_cache

import { unstable_cache } from 'next/cache'

export const getKategoriyalar = unstable_cache(
  async () => prisma.kategoriya.findMany({ orderBy: { nomi: 'asc' } }),
  ['kategoriyalar'],                       // kesh kaliti
  { revalidate: 3600, tags: ['kategoriyalar'] }
)

[!INFO] Next.js ning yangi versiyalarida unstable_cache o'rniga 'use cache' direktivasi (va cacheTag, cacheLife) taqdim etilgan — funksiya yoki komponentni to'g'ridan-to'g'ri keshlanadigan qilib belgilash. G'oya bir xil: aniq so'ralgan kesh + tag bo'yicha yangilash.


3. Full Route Cache

Build paytida statik sahifalar to'liq render qilinib saqlanadi — so'rov kelganda tayyor HTML beriladi, kod umuman bajarilmaydi.

npm run build
○ /                 — statik (Full Route Cache)
○ /blog             — statik
ƒ /dashboard        — dinamik (har so'rovda render)

Sahifa dinamik bo'ladi, agar u ishlatsa:

  • cookies(), headers(), searchParams
  • cache: 'no-store' fetch
  • export const dynamic = 'force-dynamic'

Davriy yangilash — ISR

// app/blog/page.tsx
export const revalidate = 600   // 10 daqiqada bir marta qayta render

export default async function Blog() {
  const postlar = await prisma.post.findMany({ where: { nashr: true } })
  // ...
}

Birinchi so'rov 10 daqiqadan keyin — eski sahifa darhol beriladi, fonda yangisi tayyorlanadi (16-dars, stale-while-revalidate).


4. Router Cache

Brauzerda: <Link> bilan o'tilgan va oldindan yuklangan (prefetch) sahifalar xotirada saqlanadi — "Orqaga" va qayta o'tish bir zumda.

  • Server Action dan keyin revalidatePath/revalidateTag — Router Cache ni ham tozalaydi
  • router.refresh() — joriy sahifani serverdan qayta so'rash

Talab bo'yicha yangilash

Ma'lumot o'zgarganda — kutmasdan, darhol:

'use server'

import { revalidatePath, revalidateTag } from 'next/cache'

export async function narxniYangila(id: string, narx: number) {
  await prisma.mahsulot.update({ where: { id }, data: { narx } })

  revalidateTag('mahsulotlar')                 // shu tag li barcha keshlar
  revalidatePath(`/mahsulot/${id}`)            // aniq sahifa
}
Funksiya Nimani yangilaydi
revalidatePath('/blog') Shu yo'l sahifasi
revalidatePath('/blog', 'layout') Shu layout ostidagi barcha sahifalar
revalidateTag('mahsulotlar') tags: ['mahsulotlar'] bilan keshlangan barcha ma'lumotlar

Tag lar — aniqroq: bitta mahsulot o'zgarganda, unga bog'liq barcha sahifalar (katalog, bosh sahifa, mahsulot sahifasi) bitta chaqiruv bilan.

Tashqi manbadan — CMS webhook

// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'

export async function POST(request: Request) {
  if (request.headers.get('x-secret') !== process.env.REVALIDATE_SECRET) {
    return Response.json({ xato: "Ruxsat yo'q" }, { status: 401 })
  }
  const { tag } = await request.json()
  revalidateTag(tag)
  return Response.json({ yangilandi: true })
}

Qaysi biri qachon

Ma'lumot Strategiya
Deyarli o'zgarmaydi (sahifalar, kategoriyalar) Statik + revalidateTag o'zgarganda
Vaqti-vaqti bilan (blog, katalog) revalidate = 600 + talab bo'yicha
Har doim yangi (narx, qoldiq, savat) Dinamik, no-store
Foydalanuvchiga xos (profil, buyurtmalar) Dinamik (cookies) — hech qachon umumiy keshda emas

[!WARNING] Foydalanuvchiga xos ma'lumotni unstable_cache yoki force-cache bilan umumiy kalit ostida keshlamang — bir foydalanuvchining ma'lumoti boshqasiga ko'rinib qoladi. Kesh kalitiga userId qo'shing yoki bunday ma'lumotni keshlamang.


Tez-tez uchraydigan xatolar

1. "Production da yangilanmayapti"

Development da har so'rov qayta render bo'ladi, production da sahifa statik. revalidate yoki revalidatePath/Tag.

2. Server Action dan keyin revalidate yo'q

Bazada o'zgardi, sahifa eski.

3. Hamma narsani force-dynamic

Sayt ishlaydi, lekin har so'rov — to'liq render va baza so'rovi; kesh foydasi yo'qoladi.


Xulosa

Request Memoization — bitta render da bir xil fetch bir marta; ORM uchun React.cache
Data Cache — fetch { cache: 'force-cache' | 'no-store', next: { revalidate, tags } }
           — unstable_cache / 'use cache' — ORM natijalari
Next.js 15 — fetch standart holatda keshlanmaydi
Full Route Cache — statik sahifalar; cookies/headers/searchParams → dinamik
revalidate = N — ISR; revalidatePath / revalidateTag — darhol
Router Cache — brauzerda; router.refresh()
Foydalanuvchiga xos ma'lumot — umumiy keshda emas

Keyingi dars

60-dars: Edge Runtime — nima, Node.js dan farqi, qachon kerak

Foydalanuvchiga yaqin joyda ishlaydigan yengil runtime va uning cheklovlari.