Pagination Strategies
Relevant source files
The following files were used as context for generating this wiki page:
Seaography provides a robust and flexible system for paginating database results, supporting three distinct strategies: cursor-based, page-based, and offset-based pagination. This system is designed to work seamlessly with SeaORM entities and async-graphql dynamic schemas.
The framework handles the transformation of GraphQL pagination arguments into database-level queries, including the calculation of page metadata and navigation flags (e.g., hasNextPage).
Sources: src/lib.rs:56-58, src/query/pagination.rs:13-25
Pagination Core Architecture
The pagination logic is centered around the apply_pagination function, which determines the strategy based on the provided PaginationInput. This input is structured as a "one-of" object, meaning only one strategy can be active at a time.
Pagination Process Flow
The following diagram illustrates how the system selects and applies a pagination strategy to a SeaORM Select statement.
The system first applies global configuration defaults (like maximum limits) before executing the database-specific logic for the chosen strategy.
Sources: src/query/pagination.rs:13-30, src/query/pagination.rs:414-430
Supported Strategies
1. Cursor-Based Pagination
Cursor pagination uses a unique identifier (typically the Primary Key) to fetch records after a specific point. This is the most efficient method for large datasets as it avoids the performance degradation associated with high offsets.
- Primary Key Arity: Seaography supports composite primary keys up to an arity of 3.
- Navigation: It computes
has_next_pageandhas_previous_pageby performing lookahead queries relative to the first and last nodes in the current result set. - Encoding: Cursors are encoded into strings using
encode_cursorfor transmission to the client.
Sources: src/query/pagination.rs:32-120
2. Page-Based Pagination
Page pagination calculates results based on a page number and a limit. It is implemented using SeaORM's native PaginatorTrait.
- Metadata: Returns
PaginationInfocontaining the total number of items, total pages, and the current page index. - Calculations: Automatically determines if previous or next pages exist based on the total count and current index.
Sources: src/query/pagination.rs:135-171, src/outputs/pagination_info_object.rs:8-14
3. Offset-Based Pagination
Offset pagination allows clients to skip a specific number of records.
- Total Count: It executes a
COUNT(*)subquery to determine the total record count for the provided filters. - Logic: Uses SeaORM's
.offset(n).limit(m)methods.
Sources: src/query/pagination.rs:172-224
Data Structures and Configuration
Input Objects
The PaginationInput serves as the entry point for all pagination requests.
| Field | Type | Description |
|---|---|---|
cursor | CursorInput | Contains limit and an optional cursor string. |
page | PageInput | Contains limit and page index. |
offset | OffsetInput | Contains limit and offset value. |
Sources: src/inputs/pagination_input.rs:10-15
Global Configuration
Developers can configure pagination behavior via the BuilderContext during schema initialization.
| Parameter | Type | Default | Description |
|---|---|---|---|
default_limit | Option<u64> | None | Default limit applied if none is provided. |
max_limit | Option<u64> | None | Hard limit to prevent deep-scraping or DoS. |
Sources: src/inputs/pagination_input.rs:24-38, examples/sqlite/tests/more_query_tests.rs:10-15
In-Memory Pagination
For scenarios where data is already fetched or stored in memory, Seaography provides apply_memory_pagination. This function replicates the logic of database pagination (filtering, skipping, and taking) on a standard Rust Vec.
Sources: src/query/pagination.rs:260-412
Implementation Example: Setting Limits
The following snippet demonstrates how limits are enforced and defaults are applied within the system.
fn check_limit(
context: &'static BuilderContext,
requested_limit: u64,
) -> Result<(), sea_orm::DbErr> {
if requested_limit == 0 {
return Err(sea_orm::DbErr::Query(sea_orm::RuntimeErr::Internal(
"Requested pagination limit must be greater than 0".to_string(),
)));
}
if let Some(max_limit) = context.pagination_input.max_limit {
if requested_limit > max_limit {
return Err(sea_orm::DbErr::Query(sea_orm::RuntimeErr::Internal(
format!("Requested pagination limit ({requested_limit}) exceeds maximum allowed ({max_limit})"),
)));
}
}
Ok(())
}
Sources: src/query/pagination.rs:452-475
Summary
Seaography provides a unified interface for complex pagination needs. By abstracting SeaORM's internal paginators and manual query builders into a single apply_pagination utility, it ensures consistent behavior across different database backends (MySQL, Postgres, SQLite) while protecting application performance through configurable limits and efficient cursor-based navigation.