SOP 004: Query a Database
Fresh Last Updated: January 2025| Field | Value |
|---|---|
| SOP ID | SOP-004 |
| Version | 1.0 |
| Status | Active |
| Difficulty | Intermediate |
Purpose
Query Notion databases with filters, sorts, and pagination to retrieve specific data efficiently.
Prerequisites
- [ ] Database ID from SOP 003
- [ ] Read content capability enabled
- [ ] Database shared with integration
Query Flow
Step-by-Step Instructions
Step 1: Basic Query
javascript
const { Client } = require('@notionhq/client');
require('dotenv').config();
const notion = new Client({ auth: process.env.NOTION_API_KEY });
async function queryDatabase() {
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID
});
console.log(`Found ${response.results.length} pages`);
return response.results;
}Step 2: Query with Filters
Simple Filter
javascript
// Filter by single select
const response = await notion.databases.query({
database_id: databaseId,
filter: {
property: 'Status',
select: {
equals: 'In Progress'
}
}
});Compound Filter (AND)
javascript
const response = await notion.databases.query({
database_id: databaseId,
filter: {
and: [
{
property: 'Status',
select: { equals: 'In Progress' }
},
{
property: 'Priority',
number: { greater_than: 2 }
}
]
}
});Compound Filter (OR)
javascript
const response = await notion.databases.query({
database_id: databaseId,
filter: {
or: [
{
property: 'Status',
select: { equals: 'Done' }
},
{
property: 'Archived',
checkbox: { equals: true }
}
]
}
});Step 3: Filter Types Reference
javascript
// Contains
{ property: 'Name', title: { contains: 'urgent' } }
// Starts with
{ property: 'Name', title: { starts_with: 'TASK-' } }
// Is not empty
{ property: 'Description', rich_text: { is_not_empty: true } }javascript
// Greater than
{ property: 'Priority', number: { greater_than: 3 } }
// Between (using AND)
{
and: [
{ property: 'Price', number: { greater_than_or_equal_to: 10 } },
{ property: 'Price', number: { less_than_or_equal_to: 100 } }
]
}javascript
// After specific date
{ property: 'Due Date', date: { after: '2025-01-01' } }
// Within past week
{ property: 'Created', date: { past_week: {} } }
// Is empty (no date set)
{ property: 'Due Date', date: { is_empty: true } }javascript
// Single select equals
{ property: 'Status', select: { equals: 'Done' } }
// Multi-select contains
{ property: 'Tags', multi_select: { contains: 'Urgent' } }javascript
// Checked
{ property: 'Archived', checkbox: { equals: true } }
// Not checked
{ property: 'Archived', checkbox: { equals: false } }Step 4: Sorting Results
javascript
const response = await notion.databases.query({
database_id: databaseId,
sorts: [
{
property: 'Priority',
direction: 'ascending' // or 'descending'
},
{
property: 'Due Date',
direction: 'ascending'
}
]
});Sort by Timestamps
javascript
sorts: [
{
timestamp: 'created_time',
direction: 'descending'
}
]
// Or: 'last_edited_time'Step 5: Pagination
javascript
async function getAllPages(databaseId, filter = undefined) {
const pages = [];
let hasMore = true;
let cursor = undefined;
while (hasMore) {
const response = await notion.databases.query({
database_id: databaseId,
filter,
start_cursor: cursor,
page_size: 100 // Max 100
});
pages.push(...response.results);
hasMore = response.has_more;
cursor = response.next_cursor;
console.log(`Fetched ${pages.length} pages...`);
}
return pages;
}
// Usage
const allTasks = await getAllPages(databaseId, {
property: 'Status',
select: { does_not_equal: 'Archived' }
});Step 6: Extract Property Values
javascript
function extractProperties(page) {
const props = page.properties;
return {
id: page.id,
// Title
name: props.Name?.title?.[0]?.plain_text || '',
// Select
status: props.Status?.select?.name || null,
// Number
priority: props.Priority?.number || 0,
// Date
dueDate: props['Due Date']?.date?.start || null,
// Multi-select
tags: props.Tags?.multi_select?.map(t => t.name) || [],
// Checkbox
archived: props.Archived?.checkbox || false,
// People
assignees: props.Assignee?.people?.map(p => p.name) || [],
// URL
url: page.url
};
}
// Usage
const pages = await queryDatabase();
const tasks = pages.map(extractProperties);
console.table(tasks);Complete Example
javascript
async function getActiveTasks() {
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
filter: {
and: [
{
property: 'Status',
select: { does_not_equal: 'Done' }
},
{
property: 'Archived',
checkbox: { equals: false }
}
]
},
sorts: [
{ property: 'Priority', direction: 'ascending' },
{ property: 'Due Date', direction: 'ascending' }
],
page_size: 50
});
return response.results.map(page => ({
name: page.properties.Name.title[0]?.plain_text,
status: page.properties.Status.select?.name,
priority: page.properties.Priority.number,
dueDate: page.properties['Due Date'].date?.start,
url: page.url
}));
}Verification Checklist
- [ ] Basic query returns results
- [ ] Filters reduce result set correctly
- [ ] Sorts order results as expected
- [ ] Pagination retrieves all pages
- [ ] Property extraction works for all types
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Empty results | Filter too restrictive | Broaden filter conditions |
| Wrong property | Property name mismatch | Check exact property names |
| Missing data | Pagination not implemented | Use has_more and next_cursor |
| Type error | Wrong filter type | Match filter to property type |