Paginate query results
Goal: return a page of rows plus paging metadata (total_pages, total_items, …) from a controller endpoint, instead of loading an entire table.
This builds on Query data with the condition DSL. For exact signatures and the PagerMeta shape, see Query DSL & pagination.
1. Accept pagination params in your request
PaginationQuery is a small, #[serde(flatten)]-friendly struct with two fields, page (1-based, default 1) and page_size (default 25). Flatten it into your controller’s own query-params struct so callers can pass ?page=2&page_size=10 alongside your own filters:
use loco_rs::prelude::*;use serde::Deserialize;
#[derive(Debug, Deserialize)]pub struct ListQueryParams { pub title: Option<String>, #[serde(flatten)] pub pagination: query::PaginationQuery,}2. Paginate an entity query with an optional condition
query::paginate takes a Select<E> (an unresolved entity query), an optional pre-built Condition, and a &PaginationQuery. It applies the condition for you, so don’t call .filter() yourself first:
use axum::extract::{Query, State};
pub async fn list( State(ctx): State<AppContext>, Query(params): Query<ListQueryParams>,) -> Result<Response> { let condition = params .title .as_ref() .map(|t| query::condition().contains(posts::Column::Title, t).build());
let res = query::paginate( &ctx.db, posts::Entity::find(), condition, ¶ms.pagination, ) .await?;
format::json(res)}3. Or paginate a pre-built selector with fetch_page
Use fetch_page when you’ve already composed a Select (with your own .filter()/.order_by()/joins) and just need to page over it — it takes no separate condition argument:
let selector = posts::Entity::find() .filter(query::condition().eq(posts::Column::UserId, user_id).build()) .order_by_desc(posts::Column::CreatedAt);
let res = query::fetch_page(&ctx.db, selector, &query::PaginationQuery::page(2)).await?;4. What you get back
Both functions return LocoResult<PageResponse<T>>:
pub struct PageResponse<T> { pub page: Vec<T>, pub meta: PagerMeta,}
pub struct PagerMeta { pub page: u64, pub page_size: u64, pub total_pages: u64, pub total_items: u64,}Returning res from a controller (e.g. via format::json(res)) serializes to:
{ "page": [ { "id": 1, "title": "..." }, ... ], "meta": { "page": 2, "page_size": 25, "total_pages": 4, "total_items": 87 }}Result
A request like GET /posts?page=2&page_size=10&title=loco returns exactly one page of matching rows plus enough metadata for a client to render “page 2 of 4” or build next/prev links — without loading the whole table or hand-writing OFFSET/LIMIT math. Remember page is 1-based on the way in; both functions handle the conversion to Sea-ORM’s 0-based paging internally.
Next
- Query DSL & pagination reference for
PaginationQuery’s exact defaults and thepaginate/fetch_pagesignatures. - Query data for building the
Conditionyou pass in.