@geleeroyale/notion-cms
v1.0.2
Published
A minimal framework to use Notion as CMS for any website
Downloads
9
Readme
Notion CMS Framework
A minimal framework to use Notion as a CMS for any website.
Installation
npm install notion-cmsSetup
- Create a Notion integration at https://www.notion.so/my-integrations
- Share your Notion database/pages with the integration
- Copy your integration token and database ID
Usage
import { NotionCMS } from "notion-cms";
const cms = new NotionCMS({
auth: process.env.NOTION_TOKEN,
databaseId: process.env.NOTION_DATABASE_ID,
cache: {
enabled: true,
ttl: 300, // 5 minutes
},
});
// Get all pages from a database
const { pages } = await cms.getDatabase();
// Get a single page with full content
const page = await cms.getPage("page-id");
console.log(page.html); // Rendered HTML
console.log(page.markdown); // Rendered Markdown
console.log(page.content); // Structured content blocks
// Get page by slug
const blogPost = await cms.getPageBySlug("my-first-post");
// Get all pages (handles pagination)
const allPages = await cms.getAllPages();Database Query Options
const { pages, hasMore, nextCursor } = await cms.getDatabase(undefined, {
filter: {
property: "Status",
select: { equals: "Published" },
},
sorts: [{ property: "Date", direction: "descending" }],
pageSize: 10,
});Page Content Structure
interface PageContent {
id: string;
title: string;
properties: Record<string, any>;
content: ContentBlock[]; // Structured blocks
html: string; // Pre-rendered HTML
markdown: string; // Pre-rendered Markdown
lastEdited: string;
createdTime: string;
}Supported Block Types
- Paragraphs
- Headings (H1, H2, H3)
- Bulleted and numbered lists
- To-do items
- Toggle blocks
- Code blocks (with language)
- Quotes and callouts
- Dividers
- Images, videos, embeds
- Bookmarks
- Tables
Caching
Built-in memory cache with configurable TTL:
const cms = new NotionCMS({
auth: "...",
cache: { enabled: true, ttl: 600 },
});
// Manually clear cache
cms.clearCache();
// Invalidate specific page
cms.invalidatePage("page-id");Example: Next.js Integration
// app/blog/[slug]/page.tsx
import { NotionCMS } from "notion-cms";
const cms = new NotionCMS({
auth: process.env.NOTION_TOKEN!,
databaseId: process.env.NOTION_DATABASE_ID!,
cache: { enabled: true },
});
export async function generateStaticParams() {
const pages = await cms.getAllPages();
return pages.map((page) => ({ slug: page.slug }));
}
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await cms.getPageBySlug(params.slug);
if (!post) return <div>Not found</div>;
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
);
}Example: Static Site Generation
import { NotionCMS } from "notion-cms";
import { writeFileSync } from "fs";
const cms = new NotionCMS({ auth: process.env.NOTION_TOKEN! });
async function build() {
const pages = await cms.getAllPages(process.env.NOTION_DATABASE_ID);
for (const page of pages) {
const content = await cms.getPage(page.id);
writeFileSync(`./dist/${page.slug}.html`, content.html);
}
}
build();Webhooks (Automatic Updates)
Notion can push updates directly to your app via webhooks. The framework provides handlers for all major frameworks.
Setup Webhooks in Notion
- Go to your integration settings at https://www.notion.so/my-integrations
- Enable webhooks and set your endpoint URL (e.g.,
https://yoursite.com/api/notion-webhook) - Copy the webhook signing secret
Next.js App Router
// app/api/notion-webhook/route.ts
import { NotionWebhook, NotionCMS } from "notion-cms";
const cms = new NotionCMS({
auth: process.env.NOTION_TOKEN!,
cache: { enabled: true },
});
const webhook = new NotionWebhook({
secret: process.env.NOTION_WEBHOOK_SECRET!,
onPageUpdate: async (event) => {
// Invalidate cache when page is updated
if (event.data.page_id) {
cms.invalidatePage(event.data.page_id);
}
},
onPageCreate: async (event) => {
console.log("New page created:", event.data.page_id);
},
});
export const POST = webhook.nextAppHandler();Next.js Pages Router
// pages/api/notion-webhook.ts
import { NotionWebhook } from "notion-cms";
const webhook = new NotionWebhook({
secret: process.env.NOTION_WEBHOOK_SECRET!,
onPageUpdate: async (event) => {
// Trigger revalidation
await fetch(
`${process.env.SITE_URL}/api/revalidate?pageId=${event.data.page_id}`,
);
},
});
export default webhook.nextApiHandler();Express
import express from "express";
import { NotionWebhook, NotionCMS } from "notion-cms";
const app = express();
const cms = new NotionCMS({
auth: process.env.NOTION_TOKEN!,
cache: { enabled: true },
});
const webhook = new NotionWebhook({
secret: process.env.NOTION_WEBHOOK_SECRET!,
onPageUpdate: (event) => cms.invalidatePage(event.data.page_id!),
});
app.use(express.raw({ type: "application/json" }));
app.post("/api/notion-webhook", webhook.middleware());Custom Handler
import { NotionWebhook } from "notion-cms";
const webhook = new NotionWebhook({
secret: process.env.NOTION_WEBHOOK_SECRET!,
});
// Subscribe to specific events
webhook.on("page.content_updated", async (event) => {
console.log("Page content updated:", event.data.page_id);
});
webhook.on("page.deleted", async (event) => {
console.log("Page deleted:", event.data.page_id);
});
// Catch-all handler
webhook.on("*", async (event) => {
console.log("Event received:", event.type);
});
// Manual request handling
const result = await webhook.handleRequest({
body: requestBody,
headers: requestHeaders,
});Supported Webhook Events
page.created- New page createdpage.content_updated- Page content changedpage.properties_updated- Page properties changedpage.deleted- Page deletedpage.restored- Page restored from trashpage.moved- Page moved to different parentdatabase.created- New database createddatabase.content_updated- Database content changeddatabase.properties_updated- Database schema changed
Environment Variables
NOTION_TOKEN=secret_xxxxx
NOTION_DATABASE_ID=xxxxx
NOTION_WEBHOOK_SECRET=xxxxxLicense
MIT
