Skip to content

Axum

The paginator-axum crate provides query extractors and JSON responders for Axum.

[dependencies]
paginator-axum = "0.2.2"
axum = "0.7"

Extract pagination parameters from query strings:

use axum::{Router, routing::get};
use paginator_axum::{PaginationQuery, PaginatedJson};
use serde::Serialize;
#[derive(Serialize)]
struct User {
id: u32,
name: String,
}
async fn get_users(
PaginationQuery(params): PaginationQuery,
) -> PaginatedJson<User> {
let users = vec![
User { id: 1, name: "Alice".to_string() },
User { id: 2, name: "Bob".to_string() },
];
PaginatedJson::new(users, &params, 100)
}
let app = Router::new().route("/users", get(get_users));

The extractor parses these query parameters:

ParameterTypeDefaultDescription
pageu321Page number (1-indexed)
per_pageu3220Items per page (max: 100)
sort_byString-Field to sort by
sort_directionString-asc or desc
filterString[]-Filters in field:operator:value format
searchString-Search query
search_fieldsString-Comma-separated fields to search
GET /users?page=2&per_page=20
GET /users?sort_by=name&sort_direction=asc
GET /users?filter=status:eq:active&filter=age:gt:18
GET /users?search=john&search_fields=name,email
GET /users?filter=role:in:admin,moderator&sort_by=created_at&sort_direction=desc

Filters use the format field:operator:value:

status:eq:active # Equal
age:gt:18 # Greater than
age:between:18,65 # Between
role:in:admin,mod # In array
name:like:%john% # LIKE pattern
deleted_at:is_null # IS NULL

PaginatedJson automatically serializes the response and adds pagination headers:

// With total count
PaginatedJson::new(users, &params, total_count)
X-Total-Count: 100
X-Total-Pages: 5
X-Current-Page: 1
X-Per-Page: 20

Generate RFC 5988 Link headers:

use paginator_axum::create_link_header;
let link = create_link_header(&params, total_count, "/api/users");