Paginate Endpoint results with a cursor¶
Cursor pagination, also called keyset pagination, uses values from the last row of one page to retrieve the next page. Use it to process a large result set sequentially without the increasing query cost of a large OFFSET.
This guide creates a Data Source and an API Endpoint that paginate tenant activity by timestamp and activity ID.
Before you start¶
Before you create the resources, ensure you have:
- A Tinybird project
- The Tinybird CLI installed
- A dataset with columns that define a stable, unique order
Step 1: Create the Data Source¶
Create a tenant_activity_log.datasource file:
datasources/tenant_activity_log.datasource
DESCRIPTION >
Fake tenant activity data for testing keyset pagination
SCHEMA >
`tenant_id` String `json:$.tenant_id`,
`occurred_at` DateTime64(3) `json:$.occurred_at`,
`activity_id` UInt64 `json:$.activity_id`,
`activity_type` LowCardinality(String) `json:$.activity_type`,
`details` String `json:$.details`
ENGINE "MergeTree"
ENGINE_SORTING_KEY "tenant_id, occurred_at, activity_id"
The sorting key matches how the Endpoint filters and orders the data:
tenant_ididentifies one tenant and appears first in the sorting key.occurred_atorders the tenant's activity chronologically.activity_idprovides a unique tie-breaker when multiple activities have the same timestamp.
Step 2: Create the API Endpoint¶
Create a tenant_activity_page.pipe file:
pipes/tenant_activity_page.pipe
TOKEN tenant_activity_page_read READ
NODE paginated_activity
SQL >
%
SELECT
tenant_id,
occurred_at,
activity_id,
activity_type,
details
FROM tenant_activity_log
WHERE tenant_id = {{String(tenant_id, required=True)}}
AND (occurred_at, activity_id) < (
{{DateTime64(cursor_occurred_at)}},
{{UInt64(cursor_activity_id)}}
)
ORDER BY
occurred_at DESC,
activity_id DESC
LIMIT {{UInt16(page_size, 5)}}
TYPE ENDPOINT