# 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.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.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
```
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