# init Source: https://docs.notcms.com/en/cli-commands/init Initialize a new NotCMS project ## Overview The `init` command sets up NotCMS in your project with interactive configuration. ```bash theme={null} npx notcms-kit init ``` ## What it does 1. Creates `notcms.config.json` configuration file 2. Checks environment variables in `.env` ## Options | Option | Description | Default | | ------- | --------------------- | ------------------------------------- | | `--env` | Environment file path | `[".env", ".env.local", ".dev.vars"]` | ## Interactive Setup The command will guide you through: ### 1. Configuration ``` ? Schema file path: ./src/notcms/schema.ts ``` ## Examples ### Basic initialization ```bash theme={null} npx notcms-kit init ``` ### Specify environment file path Use this if you already have `.env` configured: ```bash theme={null} npx notcms-kit init --env .env.development ``` ## Troubleshooting ## Next Steps After initialization, you can: 1. Start using the NotCMS client in your code 2. Run `npx notcms-kit pull` to update your schema # pull Source: https://docs.notcms.com/en/cli-commands/pull Generate TypeScript schema from Notion databases ## Overview The `pull` command generates or updates your TypeScript schema from Notion databases. ```bash theme={null} npx notcms-kit pull ``` ## What it does 1. Fetches database structure and properties 2. Generates TypeScript types 3. Updates your schema file ## Options | Option | Description | Default | | ------- | --------------------- | ------------------------------------- | | `--env` | Environment file path | `[".env", ".env.local", ".dev.vars"]` | ## Examples ### Basic pull ```bash theme={null} npx notcms-kit pull ``` ### Specify environment file path ```bash theme={null} npx notcms-kit pull --env .env.development ``` ## Generated Schema The command generates a fully typed schema: ```typescript theme={null} import { Schema } from 'notcms'; export const schema = { // example blog: { id: "abc123...", properties: { title: "title", content: "rich_text", published: "checkbox", publishDate: "date", author: "relation", tags: "multi_select" } } } satisfies Schema; ``` ## Schema Updates When you update Notion databases, the pull command will update your schema. ```json theme={null} { "scripts": { "schema:pull": "notcms-kit pull", } } ``` # Next.js Integration Source: https://docs.notcms.com/en/examples/nextjs Build a blog with Next.js and NotCMS # Next.js Integration Learn how to integrate NotCMS with Next.js App Router to build a fully-featured blog. ## Setup ### 1. Create Next.js Project ```bash theme={null} npx create-next-app@latest my-blog --typescript --app cd my-blog ``` ### 2. Install NotCMS ```bash theme={null} npm install notcms npm install -D notcms-kit ``` ### 3. Initialize NotCMS ```bash theme={null} npx notcms-kit init ``` ### 4. Configure Next.js Update `next.config.js`: ```javascript theme={null} /** @type {import('next').NextConfig} */ const nextConfig = { images: { remotePatterns: [ { protocol: 'https', hostname: '*.notcms.com', } ] } } module.exports = nextConfig ``` ## Project Structure ``` my-blog/ ├── app/ │ ├── page.tsx # Blog listing │ └── [id]/ │ └── page.tsx # Blog post ├── components/ │ ├── BlogCard.tsx │ ├── BlogPost.tsx │ └── Pagination.tsx └── src/ └── notcms/ └── schema.ts # Generated schema ``` ## Blog Listing Page ### `app/page.tsx` ```typescript theme={null} import { Client } from 'notcms'; import { schema } from '@/notcms/schema'; import BlogCard from '@/components/BlogCard'; const nc = new Client({ secretKey: process.env.NOTCMS_SECRET_KEY, workspaceId: process.env.NOTCMS_WORKSPACE_ID, }); export default async function BlogPage() { const [posts] = await nc.query.blog.list(); return (

Blog

{posts.map((post) => ( ))}
); } // Revalidate every 10 minutes export const revalidate = 600; ``` ## Blog Post Page ### `app/[id]/page.tsx` ```typescript theme={null} import { notFound } from 'next/navigation'; import { Client } from 'notcms'; import { schema } from '@/notcms/schema'; import BlogPost from '@/components/BlogPost'; import type { Metadata } from 'next'; interface PageProps { params: { id: string; }; } const nc = new Client({ secretKey: process.env.NOTCMS_SECRET_KEY, workspaceId: process.env.NOTCMS_WORKSPACE_ID, }); export async function generateMetadata({ params }: PageProps): Promise { const [post] = await nc.query.blog.get(params.id); if (!post) { return { title: 'Post Not Found', }; } return { title: post.title, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: post.coverImage ? [post.coverImage] : [], }, }; } export default async function BlogPostPage({ params }: PageProps) { const [post] = await nc.query.blog.get(params.id); if (!post) { notFound(); } return ; } // Revalidate every 10 minutes export const revalidate = 600; ``` ## Components ### `components/BlogCard.tsx` ```typescript theme={null} import Link from 'next/link'; import Image from 'next/image'; import { Client } from 'notcms'; import { schema } from '@/notcms/schema'; const nc = new Client({ schema, }); type BlogPosts = typeof schema.blog.pages.$inferPages; type BlogPost = BlogPosts[number]; // BlogPost: { // id: string; // title: string; // properties: { // coverImage: string; // publishDate: string; // authors: string[]; // tags: string[]; // } // } // NOTE: Above is just an example. The actual schema will reflect YOUR database schema. interface BlogCardProps { post: BlogPost; } export default function BlogCard({ post }: BlogCardProps) { return (
{post.properties.coverImage && ( {post.properties.title} )}

{post.properties.title}

{post.properties.publishDate}
{post.properties.tags.length > 0 && (
{post.properties.tags.map((tag) => ( {tag} ))}
)}
); } ``` ### `components/BlogPost.tsx` ```typescript theme={null} import Image from 'next/image'; import { RichText } from '@/components/RichText'; import { Client } from 'notcms'; import { schema } from '@/notcms/schema'; const nc = new Client({ schema, }); type BlogPost = typeof schema.blog.pages.$inferPage; // BlogPost: { // id: string; // title: string; // properties: { // coverImage: string; // publishDate: string; // authors: string[]; // tags: string[]; // } // content: string; // Markdown // } // NOTE: Above is just an example. The actual schema will reflect YOUR database schema. interface BlogPostProps { post: BlogPost; } export default function BlogPost({ post }: BlogPostProps) { return (
{post.properties.coverImage && ( {post.properties.title} )}

{post.properties.title}

{post.properties.tags.length > 0 && (
{post.properties.tags.map((tag) => ( {tag} ))}
)}
); } ``` ## Rich Text Rendering ### `components/RichText.tsx` Convert the content to HTML using a markdown parser as you like. ```typescript theme={null} import { marked } from 'marked'; interface RichTextProps { content: string; } export function RichText({ content }: RichTextProps) { if (!content) return null; return
; } ``` ## Performance Optimization ### Static Generation ```typescript theme={null} // Generate static pages at build time export async function generateStaticParams() { const [posts] = await nc.query.blog.list(); return posts.map((post) => ({ id: post.id, })); } ``` ### Image Optimization ```typescript theme={null} import Image from 'next/image'; // Use Next.js Image component {post.properties.title} ``` Don't forget to add the domain to the `next.config.js` file. ```javascript theme={null} /** @type {import('next').NextConfig} */ const nextConfig = { images: { remotePatterns: [ { protocol: 'https', hostname: '*.notcms.com', } ] } } module.exports = nextConfig ``` ## SEO Optimization ### Metadata API ```typescript theme={null} export async function generateMetadata({ params }: PageProps): Promise { const [post] = await nc.query.blog.get(params.id); return { title: post.title, description: post.properties.excerpt, authors: [{ name: post.properties.authors.join(', ') }], openGraph: { title: post.title, description: post.properties.excerpt, type: 'article', publishedTime: post.properties.publishDate, authors: [post.properties.authors.join(', ')], images: [ { url: post.properties.coverImage, width: 1200, height: 600, alt: post.properties.title, } ], }, twitter: { card: 'summary_large_image', title: post.title, description: post.properties.excerpt, images: [post.properties.coverImage], }, }; } ``` ## Deployment ### Environment Variables Set these in your deployment platform: ```env theme={null} NOTCMS_SECRET_KEY=your-secret-key NOTCMS_WORKSPACE_ID=your-workspace-id ``` ## Next Steps Use our ready-made blog template Explore usage of NotCMS # Introduction Source: https://docs.notcms.com/en/introduction Turn your Notion workspace into a type-safe headless CMS ## What is NotCMS? NotCMS is a headless CMS that uses Notion as your content backend, providing the best of both worlds: Notion's intuitive editor for content creators and a type-safe API for developers. End-to-end type safety from schema to queries with excellent developer experience. Full TypeScript support ensures no runtime type errors. Use Notion's editor your team already knows. No need to learn a new CMS interface - content creators stay productive from day one. Get started in minutes with our CLI tool. No complex configuration or infrastructure setup required. Content changes in Notion are immediately available through the API. No build step or manual sync needed. ## Core Concepts ### Type-Safe Queries Query your content with full TypeScript support: ```typescript theme={null} import { Client } from "notcms"; import { schema } from "./schema"; const nc = new Client({ schema }); // Fully typed response const [posts, error] = await nc.query.blog.list(); // posts: Array<{ // title: string, // content: string, // properties: { // published: boolean, // author: string // } // }> ``` ### Schema-First Approach NotCMS generates TypeScript schemas from your Notion databases, ensuring type safety throughout your application: ```typescript theme={null} // Auto-generated schema export const schema = { blog: { id: "your_database_id", properties: { title: "title", published: "checkbox", author: "rich_text", }, }, } satisfies Schema; ``` ### Error Handling NotCMS uses a tuple-based error handling pattern for predictable error management: ```typescript theme={null} const [data, error, response] = await nc.query.blog.get(pageId); if (error) { console.error("Failed to fetch:", error.message); return; } // Use data safely console.log(data.title); ``` ## Why NotCMS, instead of raw Notion API? While Notion's API is powerful, using it directly for content management comes with significant challenges: ### Challenges with raw Notion API * **Complex API queries**: You need to learn Notion's intricate query syntax and property structures * **No TypeScript support**: Query results lack type definitions, leading to runtime errors and poor IDE support * **Maintenance nightmare**: When you update Notion properties, you must manually update code across your entire application * **Content transformation**: You need to write custom code to convert Notion blocks to Markdown or HTML * **Image hosting issues**: Notion image URLs expire, requiring you to implement your own image re-hosting solution ### How NotCMS solves these problems NotCMS handles all these complexities for you: ```typescript theme={null} // ❌ Raw Notion API - Complex and error-prone const response = await notion.databases.query({ database_id: "abc123", filter: { property: "Published", checkbox: { equals: true }, }, }); // No TypeScript support, manual type assertions needed const title = (response.results[0].properties.Title as any).title[0].plain_text; // ✅ NotCMS - Simple and type-safe const [posts, error] = await nc.query.blog.list(); // Full TypeScript support, IDE autocomplete works posts.forEach((post) => console.log(post.title)); ``` With NotCMS, you get: * **Auto-generated TypeScript types** from your Notion databases * **Simple, intuitive API** that feels natural to JavaScript developers * **Automatic content transformation** to Markdown * **Persistent image hosting** that comes out of the box * **Instantly pull updates with CLI** when your Notion schema changes ## Next Steps Set up NotCMS in your project in under 5 minutes Install NotCMS and configure your environment Explore the complete SDK documentation See NotCMS in action with real-world examples # Quick Start Source: https://docs.notcms.com/en/quickstart Get up and running with NotCMS in under 5 minutes Set up NotCMS in your project and start fetching content from Notion in minutes. ## Prerequisites Before you begin, make sure you have: * Node.js 18.17 or higher installed * A Notion account with at least one database * Basic knowledge of TypeScript (recommended) ## Step 1: Install NotCMS ```bash npm theme={null} npm install notcms ``` ```bash yarn theme={null} yarn add notcms ``` ```bash pnpm theme={null} pnpm add notcms ``` ```bash bun theme={null} bun add notcms ``` ## Step 2: Initialize Your Project Use the NotCMS CLI to set up your project: ```bash theme={null} npx notcms-kit init ``` This command will: 1. Check environment variables 2. Create a `notcms.config.json` configuration file ## Step 3: Configure Environment Variables Add your NotCMS credentials to your `.env` file: ```env theme={null} NOTCMS_SECRET_KEY=your-secret-key NOTCMS_WORKSPACE_ID=your-workspace-id ``` Get your credentials from the [NotCMS Dashboard](https://dash.notcms.com) ## Step 4: Pull Your Schema Generate TypeScript types from your Notion databases: ```bash theme={null} npx notcms-kit pull ``` This creates a type-safe schema file at `src/notcms/schema.ts`: ```typescript theme={null} export const schema = { blog: { id: "abc123...", properties: { title: "title", published: "checkbox", author: "rich_text", tags: "multi_select" } } } satisfies Schema; ``` ## Step 5: Start Using NotCMS Create a client and start querying your content: ```typescript theme={null} import { Client } from 'notcms'; import { schema } from './notcms/schema'; const nc = new Client({ schema }); // Fetch all published blog posts const [posts, error] = await nc.query.blog.list(); if (error) { console.error("Failed to fetch posts:", error); } else { posts.forEach(post => { console.log(post.title); }); } ``` ## What's Next? Learn about advanced configuration options Explore the complete SDK API Learn how to handle errors properly See a complete Next.js integration ## Common Issues Make sure your `.env` file is in the project root and you're using a package like `dotenv` or `dotenvx` to load them. Ensure your Notion databases have the correct permissions and your API key has access to them. Run `npx notcms-kit pull` again to regenerate the schema if you've made changes to your Notion databases. # Get Single Item Source: https://docs.notcms.com/en/usage/get Fetch individual items from your Notion databases ## Basic Usage Fetch a single item by its ID: ```typescript theme={null} const [post, error] = await nc.query.blog.get('page-id-123'); if (!error) { console.log(post.title, post.content); } ``` ## Type Safety NotCMS provides full type inference for fetched items: ```typescript theme={null} // Infer single page type type BlogPost = typeof nc.query.blog.$inferPage; // Use in your functions function processBlogPost(post: BlogPost) { // Full type safety and IDE autocomplete console.log(post.title); console.log(post.published); console.log(post.author); } ``` ## Related Items Fetch related items using multiple queries: ```typescript theme={null} async function getPostWithAuthor(postId: string) { // Get the post const [post, postError] = await nc.query.blog.get(postId); if (postError) throw postError; // Get the author const [author, authorError] = await nc.query.authors.get(post.properties.authors[0]); if (authorError) throw authorError; return { ...post, author }; } ``` ## Next Steps Learn to query multiple items Explore the complete Client API # Initializing Client Source: https://docs.notcms.com/en/usage/initializing-client Learn how to set up and configure the NotCMS client ## Basic Setup To start using NotCMS, you need to initialize a client with your schema and credentials: ```typescript theme={null} import { Client } from 'notcms'; import { schema } from './notcms/schema'; const nc = new Client({ schema, apiKey: process.env.NOTCMS_SECRET_KEY, workspaceId: process.env.NOTCMS_WORKSPACE_ID }); ``` ## Prerequisites Before initializing the client, ensure you have: 1. **Generated schema** - Run `npx notcms-kit init` to generate for the first time or `npx notcms-kit pull` to update your schema 2. **Environment variables** - Set up your API credentials 3. **Installed NotCMS** - Add `notcms` to your project dependencies ## Environment Variables ### Required Variables ```env theme={null} NOTCMS_SECRET_KEY=your-secret-key NOTCMS_WORKSPACE_ID=your-workspace-id ``` ### Loading Environment Variables ```typescript theme={null} // Next.js loads .env automatically import { Client } from 'notcms'; ``` ```typescript theme={null} import dotenv from 'dotenv'; dotenv.config(); import { Client } from 'notcms'; ``` ```typescript theme={null} const nc = new Client({ schema, apiKey: import.meta.env.VITE_NOTCMS_SECRET_KEY, workspaceId: import.meta.env.VITE_NOTCMS_WORKSPACE_ID }); ``` ## Framework-Specific Setup ### Next.js App Router ```typescript theme={null} // app/lib/notcms.ts import { Client } from 'notcms'; import { schema } from '@/notcms/schema'; // Server-only client export const nc = new Client({ schema, apiKey: process.env.NOTCMS_SECRET_KEY!, workspaceId: process.env.NOTCMS_WORKSPACE_ID! }); ``` ### Remix ```typescript theme={null} // app/lib/notcms.server.ts import { Client } from 'notcms'; import { schema } from '~/notcms/schema'; // Mark as server-only with .server.ts extension export const nc = new Client({ schema, apiKey: process.env.NOTCMS_SECRET_KEY!, workspaceId: process.env.NOTCMS_WORKSPACE_ID! }); ``` ### SvelteKit ```typescript theme={null} // src/lib/notcms.ts import { Client } from 'notcms'; import { schema } from '../notcms/schema'; import { NOTCMS_SECRET_KEY, NOTCMS_WORKSPACE_ID } from '$env/static/private'; export const nc = new Client({ schema, apiKey: NOTCMS_SECRET_KEY, workspaceId: NOTCMS_WORKSPACE_ID }); ``` ## Troubleshooting ### Common Issues * Check `.env` file exists in project root * Ensure you're using a loader like `dotenv` * Verify variable names match exactly * Verify `NOTCMS_SECRET_KEY` is correct * Check `NOTCMS_WORKSPACE_ID` matches your workspace * Ensure API key has necessary permissions * Run `npx notcms-kit pull` to generate schema * Check import path is correct * Ensure schema file exports correctly * Update to latest NotCMS version * Regenerate schema with `npx notcms-kit pull` * Check TypeScript version compatibility ## Next Steps Once your client is initialized, you can: Query multiple items from your databases Fetch individual items by ID # List Items Source: https://docs.notcms.com/en/usage/list Query and list multiple items from your Notion databases ## Basic Usage Fetch all items from a database: ```typescript theme={null} const [posts, error] = await nc.query.blog.list(); if (!error) { posts.forEach(post => { console.log(post.title); }); } ``` ## Type Safety NotCMS provides full type inference for list results: ```typescript theme={null} // Infer array type type BlogPosts = typeof nc.query.blog.$inferPages; // Use in your functions function processPosts(posts: BlogPosts) { posts.forEach(post => { // Full type safety and IDE autocomplete console.log(post.title); console.log(post.published); }); } ``` ## Next Steps Learn to fetch individual items Explore the complete Client API