Notion Core Workflow A β Databases & Pages
Overview
Primary workflow for Notion integrations: querying databases with filters/sorts, creating pages with typed properties, updating page properties, and retrieving page content.
Prerequisites
- Completed
notion-install-authsetup - A Notion database shared with your integration
- Understanding of your database's property schema
Instructions
Step 1: Retrieve Database Schema
import { Client } from '@notionhq/client';
const notion = new Client({ auth: process.env.NOTION_TOKEN });
async function getDatabaseSchema(databaseId: string) {
const db = await notion.databases.retrieve({ database_id: databaseId });
// db.properties contains the schema
for (const [name, prop] of Object.entries(db.properties)) {
console.log(`${name}: ${prop.type}`);
// For select/multi_select, show options:
if (prop.type === 'select') {
console.log(' Options:', prop.select.options.map(o => o.name));
}
}
return db.properties;
}
Step 2: Query with Filters
Notion filters use a unique nested structure based on property type:
async function queryWithFilters(databaseId: string) {
const response = await notion.databases.query({
database_id: databaseId,
filter: {
and: [
{
property: 'Status',
select: { equals: 'In Progress' },
},
{
property: 'Priority',
select: { does_not_equal: 'Low' },
},
{
or: [
{
property: 'Assignee',
people: { contains: 'user-uuid-here' },
},
{
property: 'Tags',
multi_select: { contains: 'Urgent' },
},
],
},
],
},
sorts: [
{ property: 'Priority', direction: 'ascending' },
{ property: 'Created', direction: 'descending' },
],
page_size: 50,
});
return response.results;
}
Step 3: Filter Syntax by Property Type
// Text (title, rich_text, url, email, phone_number)
{ property: 'Name', title: { contains: 'search term' } }
{ property: 'Description', rich_text: { starts_with: 'Draft' } }
{ property: 'Email', email: { equals: 'user@example.com' } }
// Number
{ property: 'Score', number: { greater_than: 80 } }
{ property: 'Price', number: { less_than_or_equal_to: 100 } }
// Select / Multi-select
{ property: 'Status', select: { equals: 'Done' } }
{ property: 'Tags', multi_select: { contains: 'Bug' } }
// Date
{ property: 'Due Date', date: { before: '2026-04-01' } }
{ property: 'Created', date: { past_week: {} } }
{ property: 'Updated', date: { on_or_after: '2026-01-01' } }
// Checkbox
{ property: 'Archived', checkbox: { equals: false } }
// People
{ property: 'Assignee', people: { contains: 'user-uuid' } }
// Relation
{ property: 'Project', relation: { contains: 'page-uuid' } }
// Formula (filter on the result type)
{ property: 'Computed', formula: { number: { greater_than: 0 } } }
// Rollup (filter on the aggregated result)
{ property: 'Total', rollup: { number: { greater_than: 100 } } }
// Timestamp (no property name needed)
{ timestamp: 'last_edited_time', last_edited_time: { after: '2026-03-01' } }
Step 4: Create a Page with All Property Types
async function createFullPage(databaseId: string) {
return notion.pages.create({
parent: { database_id: databaseId },
icon: { emoji: 'π' },
properties: {
// Title (required β every database has exactly one)
Name: {
title: [{ text: { content: 'New Task' } }],
},
// Rich text
Description: {
rich_text: [
{ text: { content: 'This is ' } },
{ text: { content: 'important' }, annotations: { bold: true, color: 'red' } },
],
},
// Number
Score: { number: 95 },
// Select
Status: { select: { name: 'In Progress' } },
// Multi-select
Tags: {
multi_select: [{ name: 'API' }, { name: 'Backend' }],
},
// Date (with optional end and timezone)
'Due Date': {
date: { start: '2026-04-15', end: '2026-04-20' },
},
// Checkbox
Urgent: { checkbox: true },
// URL
Link: { url: 'https://developers.notion.com' },
// Email
Contact: { email: 'team@example.com' },
// People (array of user objects)
Assignee: {
people: [{ id: 'user-uuid-here' }],
},
// Relation (array of page references)
Project: {
relation: [{ id: 'related-page-uuid' }],
},
},
});
}
Step 5: Update Page Properties
async function updatePage(pageId: string) {
return notion.pages.update({
page_id: pageId,
properties: {
Status: { select: { name: 'Done' } },
Score: { number: 100 },
Urgent: { checkbox: false },
},
});
}
// Archive (soft delete) a page
async function archivePage(pageId: string) {
return notion.pages.update({
page_id: pageId,
archived: true,
});
}
Step 6: Paginate Through All Results
async function getAllPages(databaseId: string) {
const allPages = [];
let cursor: string | undefined = undefined;
do {
const response = await notion.databases.query({
database_id: databaseId,
start_cursor: cursor,
page_size: 100, // max is 100
});
allPages.push(...response.results);
cursor = response.has_more ? response.next_cursor ?? undefined : undefined;
} while (cursor);
return allPages;
}
Output
- Database schema retrieved with property types and options
- Filtered and sorted query results
- Pages created with typed properties
- Pages updated and archived
Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| validation_error | Property name mismatch or wrong type | Use databases.retrieve to check schema |
| object_not_found | Database not shared with integration | Add integration via Connections |
| rate_limited (429) | >3 requests/second average | Respect Retry-After header |
| Empty results | Filter too restrictive or no data | Test with no filter first |
Examples
Extract Property Values Helper
function getPropertyValue(property: any): string | number | boolean | null {
switch (property.type) {
case 'title':
return property.title.map((t: any) => t.plain_text).join('');
case 'rich_text':
return property.rich_text.map((t: any) => t.plain_text).join('');
case 'number':
return property.number;
case 'select':
return property.select?.name ?? null;
case 'multi_select':
return property.multi_select.map((s: any) => s.name).join(', ');
case 'date':
return property.date?.start ?? null;
case 'checkbox':
return property.checkbox;
case 'url':
return property.url;
case 'email':
return property.email;
case 'formula':
return property.formula?.[property.formula.type] ?? null;
default:
return null;
}
}
Resources
Next Steps
For block-level content operations, see notion-core-workflow-b.