Draft Mode — CMS bilan preview rejimi
Muammodan boshlaylik
Blog kontenti CMS da (Sanity, Contentful, Strapi, Payload). Sahifalar statik — tez va arzon (59-dars). Muharrir yangi maqola yozdi va so'raydi: "Nashr qilishdan oldin saytda qanday ko'rinishini ko'rsam bo'ladimi?"
- Sahifa statik — u faqat nashr qilingan kontentni ko'rsatadi
- Qoralamani ko'rish uchun nashr qilish kerak — lekin u hali tayyor emas
- Butun saytni dinamik qilish — hamma uchun sekinlashadi
Draft Mode nima?
Draft Mode — maxsus cookie orqali faqat shu foydalanuvchi uchun statik sahifalarni dinamik render qilish va qoralama kontentni ko'rsatish.
Oddiy tashrif → statik sahifa, nashr qilingan kontent (kesh)
Draft Mode cookie bilan → har so'rovda render, qoralama kontent
Boshqa barcha tashrif buyuruvchilar uchun sayt avvalgidek tez va statik qoladi.
Hayotiy o'xshatma
Gazeta tahririyati. Chop etilgan gazeta — hamma uchun bir xil (statik). Bosh muharrir esa maxsus ruxsatnoma bilan bosmaxonaga kirib, ertangi sonning sinov nusxasini ko'radi (draft). Ruxsatnomasiz odam faqat chiqqan gazetani oladi.
1. Yoqish uchun Route Handler
// app/api/draft/route.ts
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const maxfiy = searchParams.get('secret')
const slug = searchParams.get('slug')
if (maxfiy !== process.env.DRAFT_SECRET || !slug) {
return new Response("Noto'g'ri token", { status: 401 })
}
// slug haqiqatan mavjudligini CMS dan tekshirish
const post = await cms.getPost(slug, { draft: true })
if (!post) return new Response('Topilmadi', { status: 404 })
const draft = await draftMode()
draft.enable()
redirect(`/blog/${post.slug}`)
}
CMS da "Preview" tugmasi URL i:
https://dokon.uz/api/draft?secret=MAXFIY_TOKEN&slug=yangi-maqola
[!WARNING]
redirectga foydalanuvchi berganslugni to'g'ridan-to'g'ri qo'ymang — avval CMS dan topib, uning slug idan foydalaning. Aks holda?slug=//boshqa-sayt.uzkabi so'rov bilan sizning domeningiz orqali begona saytga yo'naltirish mumkin (open redirect).
2. Sahifada qoralamani olish
// app/blog/[slug]/page.tsx
import { draftMode } from 'next/headers'
import { notFound } from 'next/navigation'
export async function generateStaticParams() {
const postlar = await cms.getPostlar()
return postlar.map((p) => ({ slug: p.slug }))
}
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const { isEnabled } = await draftMode()
const post = await cms.getPost(slug, { draft: isEnabled })
if (!post) notFound()
return (
<article>
{isEnabled && (
<div role="status" className="bg-amber-100 p-2 text-sm">
Qoralama rejimi — <a href="/api/draft/ochir">chiqish</a>
</div>
)}
<h1>{post.sarlavha}</h1>
<div>{post.matn}</div>
</article>
)
}
isEnabled = false— oddiy statik sahifa, nashr qilingan kontentisEnabled = true— Next.js shu so'rov uchun sahifani dinamik render qiladi; CMS dan qoralama olinadi
CMS so'rovi
// lib/cms.ts — umumiy g'oya
export async function getPost(slug: string, { draft = false } = {}) {
const res = await fetch(`${CMS_URL}/posts?slug=${encodeURIComponent(slug)}&draft=${draft}`, {
headers: { Authorization: `Bearer ${draft ? process.env.CMS_PREVIEW_TOKEN : process.env.CMS_TOKEN}` },
next: draft ? undefined : { tags: ['postlar'] },
cache: draft ? 'no-store' : 'force-cache',
})
const data = await res.json()
return data[0] ?? null
}
Qoralama uchun alohida (kengroq huquqli) token va kesh siz so'rov.
3. O'chirish
// app/api/draft/ochir/route.ts
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
export async function GET() {
const draft = await draftMode()
draft.disable()
redirect('/')
}
Nashr qilinganda — keshni yangilash
Muharrir "Publish" ni bosganda CMS webhook yuboradi (35-dars):
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'
export async function POST(request: Request) {
if (request.headers.get('x-webhook-secret') !== process.env.CMS_WEBHOOK_SECRET) {
return new Response("Ruxsat yo'q", { status: 401 })
}
revalidateTag('postlar')
return Response.json({ ok: true })
}
To'liq oqim:
Muharrir yozadi → "Preview" → /api/draft → cookie → qoralama ko'rinadi
Muharrir "Publish" → CMS webhook → revalidateTag → hamma yangi versiyani ko'radi
[!INFO] Ko'p CMS lar (Sanity, Contentful, Storyblok, Payload) Next.js uchun tayyor integratsiya beradi: "Visual editing" — muharrir sayt ustida to'g'ridan-to'g'ri bosib tahrirlaydi, o'zgarishlar real vaqtda ko'rinadi. Ularning ichida ham aynan shu Draft Mode ishlaydi.
[!TIP] Draft Mode cookie si sessiya cookie si — brauzer yopilganda o'chadi. Muharrirga "Chiqish" havolasini ko'rinarli joyda ko'rsating: aks holda u qoralama ko'rinishini haqiqiy sayt deb o'ylashi mumkin.
Tez-tez uchraydigan xatolar
1. Maxfiy token siz
/api/draft ochiq — har kim qoralamalarni (masalan, e'lon qilinmagan aksiya narxlarini) ko'ra oladi.
2. Qoralama so'rovi keshlanadi
force-cache qoralama uchun ham — muharrir eski qoralamani ko'radi. Draft da no-store.
3. Open redirect
redirect(searchParams.get('slug')) — tekshiruvsiz.
Xulosa
Draft Mode — cookie orqali faqat shu foydalanuvchi uchun dinamik render va qoralama
/api/draft: secret tekshiruvi → CMS dan topish → draftMode().enable() → redirect
Sahifada: const { isEnabled } = await draftMode() → cms.getPost(slug, { draft })
Qoralama — preview token, no-store; oddiy — force-cache + tags
/api/draft/ochir — disable(); "Qoralama rejimi" belgisini ko'rsating
Publish → webhook → revalidateTag
Keyingi dars
74-dars: OpenTelemetry — monitoring va tracing
So'rov ilova ichida qayerda vaqt sarflaganini ko'rish.