ISR — Incremental Static Regeneration, revalidate, on-demand
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.