Skills Productivity Managing Notion Databases and Pages

Managing Notion Databases and Pages

v20260423
notion-core-workflow-a
This comprehensive workflow provides core CRUD (Create, Read, Update, Delete) operations for Notion integrations. It allows users to perform complex database queries using nested filters (AND/OR) across various property types (select, date, number, etc.). Furthermore, it facilitates the structured creation and updating of pages, making it essential for automating data management, content generation, and complex information retrieval within Notion.
Get Skill
225 downloads
Overview

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-auth setup
  • 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.

Info
Category Productivity
Name notion-core-workflow-a
Version v20260423
Size 7.98KB
Updated At 2026-04-28
Language