Notion ORMGitHub

Notion ORM

A Notion API TypeScript wrapper that leverages static types to deliver a better database (and custom agents) experience

Key Features

  • Full type inference when interacting with databases, including findMany, create, update, delete, and more
  • Manage databases and agents in Notion
  • Sync remote schema changes in a single command
  • Quickly start/resume chat streams with your agents
  • Access exported property values, schemas, and types (ex. zod validators)
  • Validate config and other untrusted input with zod

Setup

Example
bun add @haustle/notion-orm

Initialize config from your project root (recommended):

Example
bun notion init
notion.config.ts (Root)
// If you don't have an API key, sign up for free
// [here](https://developers.notion.com)

const auth = process.env.NOTION_KEY || "your-notion-api-key-here";
const NotionConfig = {
  auth,
  databases: [
    // Use: notion add <database-id>
  ],
  agents: [
    // Auto-populated by: notion sync
  ],
};

export default NotionConfig;

Generated config from `notion init`

Adding databases

Add new database to track and generate static types (ex. how to find ID here )

Example
bun notion add <database-id>

Adding agents (paid feature)

Agent support requires the Notion Agents SDK, which is currently in alpha and not published to npm. Because of this, a one-command setup handles the entire download-and-install flow for you:

Example
bun notion setup-agents-sdk

What this does:

  1. Clones the SDK repository into a local cache (node_modules/.cache/.notion-agents-sdk)
  2. Installs the SDK's dependencies and builds it
  3. Adds the built @notionhq/agents-client package to your project

After setup, run notion sync to generate agent types. Agents linked to your integration are automatically discovered.

Updating: When the upstream SDK receives changes, rerun the same command. It pulls the latest from the cached clone, rebuilds, and reinstalls:

Example
bun notion setup-agents-sdk
bun notion sync

If you have not run the setup command, notion sync will skip agent generation and only produce database types. Once the SDK is published to npm, this step will no longer be necessary.

Learn more about Custom Agents in the Notion documentation.

Full sync command (notion sync)

  • Fetch/refresh database schemas. If the agents SDK is installed, also syncs custom agents.
Example
bun notion sync

Initialization

Create a single ORM instance with your Notion integration key:

Example
import NotionORM from "@haustle/notion-orm";

const notion = new NotionORM({
  auth: process.env.NOTION_KEY!,
});

const db = notion.databases.yourDatabaseName; // DatabaseClient
const agent = notion.agents.yourAgentName; // AgentClient (after setup-agents-sdk)

Generated database and agent names are camelCased and exposed on an instance of NotionORM.

  • Use notion.databases.<camelCaseDatabaseName> for database operations (findMany, create, update, delete, …).
  • Use notion.agents.<camelCaseAgentName> for chat(), chatStream(), thread helpers, and history APIs (requires notion setup-agents-sdk).

Databases

Every generated database exposes a Prisma-style API with full type inference from your schema. Here are a few highlights — see the full API reference for findFirst, findUnique, count, createMany, updateMany, upsert, deleteMany, and more.

Create a page

Example
await notion.databases.books.create({
  properties: {
    bookName: "Creativity, Inc.",
    author: "Ed Catmull",
    genre: ["Non-fiction"],
    numberOfPages: 368,
    publishDate: { start: "2014-04-08" },
  },
  icon: { type: "emoji", emoji: "šŸ“•" },
});

Create a page with markdown content

Add body content to a page using Notion's enhanced markdown format. Headings, lists, code blocks, quotes, and checklists are all supported.

Example
await notion.databases.books.create({
  properties: {
    bookName: "Reading Notes",
  },
  markdown: "# Key Takeaways\n\n- Creativity requires candor\n- Protect the new\n\n> \"Quality is the best business plan.\"",
});

Find many with filters

Example
const books = await notion.databases.books.findMany({
  where: {
    and: [
      { genre: { contains: "Non-fiction" } },
      { publishDate: { on_or_after: "2024-01-01" } },
    ],
  },
  sortBy: [{ property: "bookName", direction: "ascending" }],
  select: ["bookName", "genre"],
});

Update by ID

Example
await notion.databases.books.update({
  where: { id: "page-id" },
  properties: { status: "Done", numberOfPages: 460 },
});

Delete by filter

Example
await notion.databases.books.deleteMany({
  where: { status: { equals: "Archived" } },
});

Stream large result sets

Example
for await (const book of notion.databases.books.findMany({ stream: 50 })) {
  console.log(book.bookName);
}

Cursor pagination

Example
// First page (after: null); next pages use prev.nextCursor
const first = await notion.databases.books.findMany({ after: null, size: 10 });
const next = await notion.databases.books.findMany({
  after: first.nextCursor,
  size: 10,
});
// first.data, first.hasMore, next.data

Agents

Agents require the Notion Agents SDK. Run notion setup-agents-sdk first, then notion sync to generate agent types.

Once set up, agents shared with your integration are exposed at notion.agents.*.

See the Agent client API reference for full method signatures, thread helpers, and message APIs.

Chat and read messages

Example
const chat = await notion.agents.helpBot.chat({
  message: "Is the company closed today",
});
await notion.agents.helpBot.pollThread(chat.threadId);
const messages = await notion.agents.helpBot.getMessages(chat.threadId, {
  role: "agent",
});

Stream chat

Example
const thread = await notion.agents.helpBot.chatStream({
  message: "How can I update my shipping address?",
  onMessage: (msg) => {
    if (msg.role === "agent") process.stdout.write(msg.content);
  },
});

Basic chat (non-streaming)

  • Useful when you want a straightforward request/response flow.
  • Helpful when you plan to fetch message history after completion.
Example
const chat = await notion.agents.yourAgentName.chat({
  message: "Give me a summary of this month",
});

await notion.agents.yourAgentName.pollThread(chat.threadId);

const messages = await notion.agents.yourAgentName.getMessages(chat.threadId, {
  role: "agent",
});

Continue an existing thread

  • Useful when you want to preserve context across follow-up prompts.
  • Helpful for chat UIs where users continue the same conversation.
Example
const nextTurn = await notion.agents.yourAgentName.chat({
  threadId: chat.threadId,
  message: "Now turn that into a grocery list.",
});

Streaming patterns

How to start a new chat stream (pass threadId to resume):

Example
import { AgentClient } from "@haustle/notion-orm";

const thread = await notion.agents.yourAgentName.chatStream({
  message: "Generate a prep list for that plan.",

  onMessage: (msg) => {
    if (msg.role === "agent") process.stdout.write(msg.content);
  },
});


const finalResponse = AgentClient.getAgentResponse(thread);
console.log("Thread ID:", thread.threadId);
console.log("Final:", finalResponse);

See API Reference for full method signatures, ThreadInfo shape, and message schemas.

Size & dependencies

425.2 kB is unpacked size (npm pack --dry-run).

Runtime dependencies:

  • @notionhq/client — Notion API client used by runtime DatabaseClient and AgentClient to call official endpoints.
  • @babel/parser — Parses generated TypeScript/JavaScript source into AST nodes during codegen and sync flows.
  • @babel/generator — Emits formatted source code from AST transforms used by generated config/registry/type files.
  • zod — Validates config and untrusted boundary input with typed schemas and clear parse errors.

Project Structure

Example
.
ā”œā”€ā”€ src
│   ā”œā”€ā”€ cli              # notion init / add / sync / setup-agents-sdk
│   ā”œā”€ā”€ config           # config discovery, loading, and validation
│   ā”œā”€ā”€ client           # runtime DatabaseClient + AgentClient
│   │   └── query        # typed filters + response simplification
│   ā”œā”€ā”€ ast              # code generation internals
│   │   ā”œā”€ā”€ database
│   │   ā”œā”€ā”€ agents
│   │   └── shared
│   └── types            # local type bridges
ā”œā”€ā”€ tests                # runtime, codegen, types, and CLI coverage
ā”œā”€ā”€ plugins              # lint/tooling helpers
└── build                # generated output (after build/sync)
    ā”œā”€ā”€ src
    ā”œā”€ā”€ db
    └── agents