Notion'ni headless CMS sifatida: dependency'siz, pullik xizmatlarsiz
Blog uchun CMS tanlash — kichik sayt egasining eng ko'p vaqt yeydigan qarorlaridan biri. Strapi o'rnatasanmi — server boqish kerak. Contentful olasanmi — pullik tarif kutib turibdi. Markdown fayllar bilan ketasanmi — har post uchun commit + deploy.
Men uchinchi yo'lni tanladim: Notion'ning o'zi CMS, sayt esa uni API orqali o'qiydi. Bir yildan beri madrimov.uz shu sxemada ishlaydi — nol server, nol qo'shimcha xarajat, maqola chiqarish esa "Published" checkbox'ini belgilashdan iborat.
Nega aynan Notion?
- Yozish tajribasi tayyor — editor, mobil ilova, versiyalar tarixi bor.
- Database ustunlari meta uchun ideal: slug, teglar, til, sana — hammasi property.
- API bepul va barqaror; shaxsiy blog hajmida limitlarga urilmaysan.
Muhimi: bu yechim uchun birorta npm paket kerak emas. @notionhq/client SDK'si qulay, lekin oddiy fetch ham xuddi shu ishni qiladi.
Yadro: 60 qatorlik mijoz
Notion database'ini o'qish — bitta POST so'rov. Pagination'ni unutmaslik kerak, xolos:
const NOTION_API = "https://api.notion.com/v1";
async function queryAll(dbId: string) {
const pages = [];
let cursor: string | undefined;
do {
const res = await fetch(`${NOTION_API}/databases/${dbId}/query`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.NOTION_TOKEN}`,
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
},
body: JSON.stringify({
filter: { property: "Published", checkbox: { equals: true } },
...(cursor ? { start_cursor: cursor } : {}),
}),
next: { revalidate: 300 },
});
if (!res.ok) break;
const data = await res.json();
pages.push(...data.results);
cursor = data.has_more ? data.next_cursor : undefined;
} while (cursor);
return pages;
}E'tibor ber: next: { revalidate: 300 } — bu Next.js'ning ISR mexanizmi. Sayt Notion'ga har so'rovda emas, 5 daqiqada bir marta murojaat qiladi. Notion yiqilsa ham sayt eski keshdan xizmat qilaveradi.
Bloklarni render qilish
Notion sahifa tarkibini "blok"lar daraxti sifatida qaytaradi: paragraph, heading, kod, ro'yxat. Ularni React elementlarga aylantirish — oddiy switch:
function renderBlock(block: any) {
switch (block.type) {
case "paragraph":
return <p>{richText(block.paragraph.rich_text)}</p>;
case "heading_2":
return <h2>{richText(block.heading_2.rich_text)}</h2>;
case "code":
return (
<pre>
<code>{plain(block.code.rich_text)}</code>
</pre>
);
default:
return null;
}
}Hammasini qo'llab-quvvatlash shart emas — o'zing ishlatadigan 8-10 blok turi yetadi. Qolganlari null qaytarsa, sayt yiqilmaydi.
Uch qoida, bir yildan keyin
- Property nomlarini kod bilan qat'iy moslashtir. Notion'da ustunni qayta nomlasang, sayt jimgina bo'sh qaytadi. Bu eng ko'p uchraydigan "bug".
- Har doim `try/catch` + bo'sh massiv fallback. Notion API sekinlashsa yoki 500 bersa, blog sahifasi 200 bilan bo'sh ro'yxat ko'rsatgani — 500 xatodan yaxshi.
- Rasmlar uchun signed URL muddatini unutma. Notion fayllari ~1 soatlik imzolangan havola bilan keladi — ISR intervaling undan qisqa bo'lsa, muammo o'z-o'zidan hal.
Xulosa
Notion + fetch + ISR — shaxsiy blog uchun yetarli va ortiqchasiz CMS. Server yo'q, obuna yo'q, deploy'siz kontent. Keyingi bosqichda shu sxema ustiga avtomatik tarqatish (Telegram, LinkedIn) ham quriladi — lekin bu boshqa maqola mavzusi.