ISR — Incremental Static Regeneration, revalidate, on-demand

~20 daq · Next.js · Rendering Strategiyalari

Muammodan boshlaylik

Siz yangiliklar saytini yozdingiz. Har kuni 50-100 ta yangi maqola chiqadi.

Static Rendering ishlatdingiz — tez, SEO yaxshi. Lekin muammo: yangi maqola qo'shilganda saytni qayta deploy qilish kerak. Deploy 2-3 daqiqa oladi. Bu vaqt ichida yangi maqola ko'rinmaydi.

Dynamic Rendering ishlatdingiz — har so'rovda server ishlaydi. Minglab foydalanuvchi bir vaqtda kirsa — server yuki oshadi, sahifa sekinlashadi.

Ikkalasining o'rtasidagi yechim — ISR.


ISR qanday ishlaydi

ISR — Static Rendering ga "muddati" qo'shish. Sahifa statik saqlanadi, lekin belgilangan vaqtdan keyin avtomatik yangilanadi.

Build vaqtida: HTML yaratiladi, saqlanadi
      ↓
Foydalanuvchi kiradi: tayyor HTML (tez!)
      ↓
60 soniya o'tdi
      ↓
Keyingi so'rov keldi:
  → Eski HTML darhol ko'rsatiladi (foydalanuvchi kutmaydi)
  → Orqa fonda yangi HTML yaratiladi
      ↓
Undan keyingi so'rov: yangi HTML ko'rsatiladi

Bu stale-while-revalidate strategiyasi deyiladi — "eski ma'lumotni ko'rsat, orqa fonda yangilash".


revalidate — vaqt asosida yangilanish

fetch da next.revalidate qo'yish yetarli:

// app/news/page.tsx
export default async function NewsPage() {
  const news = await fetch('https://api.example.com/news', {
    next: { revalidate: 60 }  // 60 soniyada bir yangilanadi
  }).then(r => r.json())

  return (
    <ul>
      {news.map(item => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  )
}

revalidate: 60 — "bu ma'lumotni 60 soniya keshda saqlash, keyin yangilash".

Sahifa darajasida ham belgilash mumkin:

// Butun sahifa uchun — ichidagi barcha fetch larga qo'llanadi
export const revalidate = 3600  // 1 soatda bir

export default async function Page() {
  const data = await fetch('https://api.example.com/data').then(r => r.json())
  return <div>{data.title}</div>
}

revalidate qiymatini qanday tanlash

Yangiliklar sayti        → 60-300 soniya
E-commerce mahsulotlar   → 3600 soniya (1 soat)
Blog postlar             → 86400 soniya (1 kun) yoki on-demand
Valyuta kurslari         → 60 soniya
Sport natijalari         → 30 soniya

Qoida: ma'lumot qanchalik tez-tez o'zgarsa — revalidate shunchalik kichik.


On-demand revalidation — kerak bo'lganda yangilash

Vaqt asosida yangilanish har doim mos emas. Masalan, blog post tahrirlandi — 1 soat kutmasdan darhol yangilanishi kerak.

Buning uchun on-demand revalidation — siz xohlagan vaqtda yangilash:

revalidatePath — sahifani yangilash

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache'
import { NextRequest } from 'next/server'

export async function POST(request: NextRequest) {
  const { path, secret } = await request.json()

  // Xavfsizlik — faqat to'g'ri secret bilan
  if (secret !== process.env.REVALIDATE_SECRET) {
    return Response.json({ error: 'Ruxsat yo\'q' }, { status: 401 })
  }

  revalidatePath(path)  // masalan: '/blog/nextjs-kirish'
  return Response.json({ revalidated: true })
}

CMS (Contentful, Sanity) da post tahrirlanganda bu endpoint ga webhook yuboradi:

CMS → POST /api/revalidate → { path: '/blog/nextjs-kirish' }
                           → Next.js o'sha sahifani yangilaydi

revalidateTag — teglar orqali yangilash

Ko'p sahifada bir xil ma'lumot ishlatilsa — teglar bilan yangilash qulay:

// Ma'lumot olishda teg qo'shish
const posts = await fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] }
}).then(r => r.json())

const post = await fetch(`https://api.example.com/posts/${slug}`, {
  next: { tags: ['posts', `post-${slug}`] }
}).then(r => r.json())
// Yangilash — 'posts' tegi bilan barcha sahifalar yangilanadi
import { revalidateTag } from 'next/cache'

export async function POST(request: NextRequest) {
  revalidateTag('posts')  // barcha 'posts' tegli sahifalar yangilanadi
  return Response.json({ revalidated: true })
}

Yangi post qo'shilganda — revalidateTag('posts') chaqiriladi. Barcha blog sahifalari yangilanadi.


Server Action da revalidation

API endpoint yozmasdan, to'g'ridan-to'g'ri Server Action da ham yangilash mumkin:

// app/admin/posts/page.tsx
import { revalidatePath } from 'next/cache'

async function publishPost(formData: FormData) {
  'use server'

  const title = formData.get('title') as string
  await db.createPost({ title })

  revalidatePath('/blog')  // blog sahifasini yangilash
}

export default function AdminPage() {
  return (
    <form action={publishPost}>
      <input name="title" placeholder="Post sarlavhasi" />
      <button type="submit">Nashr qilish</button>
    </form>
  )
}

Post nashr qilinishi bilan /blog sahifasi yangilanadi — deploy kerak emas.


ISR, Static, Dynamic — taqqoslash

                Static      ISR           Dynamic
──────────────────────────────────────────────────
Tezlik          Eng tez     Tez           O'rta
SEO             ✅          ✅            ✅
Server yuki     Yo'q        Kam           Ko'p
Yangilanish     Deploy      Avtomatik     Har so'rov
Foydalanuvchiga xos  ❌     ❌            ✅

ISR — Static va Dynamic ning eng yaxshi tomonlarini birlashtiradi. Ko'p hollarda eng maqbul tanlov.


Tez-tez uchraydigan xatolar

1. revalidate: 0 — bu ISR emas, Dynamic

// ❌ — bu ISR emas, Dynamic Rendering ga teng
next: { revalidate: 0 }

// ✅ — Dynamic uchun
cache: 'no-store'

2. On-demand revalidation da secret yo'q

// ❌ — har kim sahifani yangilay oladi
export async function POST() {
  revalidatePath('/blog')
  return Response.json({ ok: true })
}

// ✅ — secret tekshiruvi
if (secret !== process.env.REVALIDATE_SECRET) {
  return Response.json({ error: 'Ruxsat yo\'q' }, { status: 401 })
}

3. revalidatePath da noto'g'ri path

// ❌ — dynamic segment bilan to'liq path
revalidatePath('/blog/[slug]')

// ✅ — aniq path yoki barcha variantlar
revalidatePath('/blog/nextjs-kirish')  // bitta sahifa
revalidatePath('/blog', 'page')        // barcha /blog/* sahifalari

Xulosa

ISR — Static Rendering + avtomatik yangilanish.

revalidate: 60          → 60 soniyada bir yangilanadi
revalidatePath('/blog') → o'sha sahifani darhol yangilash
revalidateTag('posts')  → o'sha teg bilan barcha sahifalarni yangilash

Stale-while-revalidate: eski HTML darhol ko'rsatiladi, orqa fonda yangi yaratiladi. Foydalanuvchi kutmaydi.

Ko'p hollarda eng maqbul tanlov — Static tezligi + ma'lumot yangiligi.


Keyingi dars

17-dars: Server Component da fetch — caching, revalidate, no-store

Ikkinchi bo'lim tugadi. Uchinchi bo'lim — Ma'lumot Olish. fetch ning Next.js da qanday ishlashi, kesh qanday boshqariladi.