Skip to content

Commit e027b90

Browse files
committed
Add Pagination
1 parent 0fb2bc2 commit e027b90

18 files changed

Lines changed: 166 additions & 8 deletions

content/en/labs/a5.routes-and-error-responses.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ use super::payload::BookRequest;
207207
use crate::{errors::Error, models::Book, state::AppState};
208208

209209
pub async fn list(State(mut state): State<AppState>) -> Result<impl IntoResponse, Error> {
210-
let (limit, offset) = (100, 0);
210+
let (limit, offset) = (10, 0);
211211
let books =
212212
Book::all().limit(limit).offset(offset).exec(&mut state.db).await.map_err(|err| {
213213
error!(target: "database", "failed to fetch: {err:?}");
@@ -341,3 +341,95 @@ pub fn init(state: AppState) -> Router {
341341
.with_state(state)
342342
}
343343
```
344+
345+
## Support Pagination
346+
347+
### Add the Pagination
348+
349+
Add a new file `book_service/src/app/shared/pagination.rs`.
350+
351+
```rust
352+
use serde::Deserialize;
353+
354+
const DEFAULT_PER_PAGE: usize = 10;
355+
const MAX_PER_PAGE: usize = 100;
356+
357+
#[derive(Debug, Clone, Deserialize)]
358+
pub struct Pagination {
359+
#[serde(default = "default_per_page")]
360+
pub per_page: usize,
361+
362+
#[serde(default = "default_page")]
363+
pub page: usize,
364+
}
365+
366+
fn default_per_page() -> usize {
367+
DEFAULT_PER_PAGE
368+
}
369+
370+
fn default_page() -> usize {
371+
1
372+
}
373+
374+
impl Pagination {
375+
pub fn limit_offset(self) -> (usize, usize) {
376+
let limit = self.per_page.clamp(1, MAX_PER_PAGE);
377+
let page = std::cmp::max(1, self.page);
378+
let offset = (page - 1) * limit;
379+
(limit, offset)
380+
}
381+
}
382+
```
383+
384+
Add `book_service/src/app/shared/mod.rs`.
385+
386+
```rust
387+
mod pagination;
388+
389+
pub use pagination::Pagination;
390+
```
391+
392+
Add `pub mod shared;` to `book_service/src/app/mod.rs`.
393+
394+
### Attach it to Handler
395+
396+
Update `book_service/src/app/book/handler.rs`.
397+
398+
```rust
399+
use axum::{
400+
Json,
401+
extract::{Path, Query, State},
402+
http::StatusCode,
403+
response::IntoResponse,
404+
};
405+
use tracing::{error, info};
406+
use uuid::Uuid;
407+
408+
use super::payload::BookRequest;
409+
use crate::{app::shared::Pagination, errors::Error, models::Book, state::AppState};
410+
411+
pub async fn list(
412+
State(mut state): State<AppState>,
413+
Query(pagination): Query<Pagination>,
414+
) -> Result<impl IntoResponse, Error> {
415+
let (limit, offset) = pagination.limit_offset();
416+
let books =
417+
Book::all().limit(limit).offset(offset).exec(&mut state.db).await.map_err(|err| {
418+
error!(target: "database", "failed to fetch: {err:?}");
419+
Error::DbFetch
420+
})?;
421+
Ok((StatusCode::OK, Json(books)))
422+
}
423+
```
424+
425+
## Update justfile
426+
427+
Update `book_service/justfile` to add custom `DB_HOST` locally.
428+
429+
```justfile
430+
# Run server app
431+
app:
432+
@export $(grep -v '^#' .env | xargs) && \
433+
DB_HOST={{ db_host }} \
434+
cargo run --bin app
435+
```

docs/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!doctype html><html lang=en-US><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Rust Programming Language Tutorials for Everyone!"><meta name=author content="Dumindu Madunuwan"><meta name=theme-color content="#ffffff" media="(prefers-color-scheme: light)"><meta name=theme-color content="#101010" media="(prefers-color-scheme: dark)"><title>Learning Rust · Rust Programming Language Tutorials for Everyone!</title><link rel=canonical href=https://learning-rust.github.io/><link rel=stylesheet href=/assets/css/home.min.2f64b534694f7783ee00c07c45f130ef260d59af33aefe9e41a6606a53c5de38.css integrity><link href=https://learning-rust.github.io/pagefind/pagefind-component-ui.css rel=stylesheet><link rel=manifest href=/manifest.json><link rel=icon href=/favicon/favicon.ico><link rel=icon href=/favicon/favicon-16x16.png sizes=16x16 type=image/png><link rel=icon href=/favicon/favicon-32x32.png sizes=32x32 type=image/png><link rel=apple-touch-icon href=/favicon/apple-touch-icon.png sizes=180x180><script async src="https://www.googletagmanager.com/gtag/js?id=G-FZHQCXSZ89"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-FZHQCXSZ89")</script><meta property="og:title" content="Learning Rust"><meta property="og:description" content="Learning Rust - Rust Programming Language Tutorials for Everyone!"><meta property="og:type" content="article"><meta property="og:url" content="https://learning-rust.github.io/"><meta property="og:image" content="https://learning-rust.github.io/og.jpg"><meta property="og:site_name" content="Learning Rust"><meta name=twitter:card content="summary_large_image"><meta name=twitter:title content="Learning Rust"><meta name=twitter:description content="Learning Rust - Rust Programming Language Tutorials for Everyone!"><meta name=twitter:image content="https://learning-rust.github.io/og.jpg"><script type=application/ld+json>{"@context":"https://schema.org","@type":"Article","headline":"Learning Rust","description":"Learning Rust - Rust Programming Language Tutorials for Everyone!","image":"https:\/\/learning-rust.github.io\/og.jpg","author":{"@type":"Person","name":"Dumindu Madunuwan","url":"https:\/\/github.com\/dumindu"},"publisher":{"@type":"Organization","name":"Learning Rust","logo":{"@type":"ImageObject","url":"https:\/\/learning-rust.github.io\/"}},"dateModified":"2026-08-16T02:50:45Z","mainEntityOfPage":{"@type":"WebPage","@id":"https:\/\/learning-rust.github.io\/"}}</script></head><body><div id=content-wrapper><header id=site-header><div id=site-header-logo><a href=https://learning-rust.github.io/><img height=52px src=https://learning-rust.github.io/logo.svg alt="Site Logo"></a></div><div id=site-header-actions><div id=search-box><pagefind-modal-trigger compact=true></pagefind-modal-trigger><pagefind-modal></pagefind-modal></div><div id=theme-dropdown class=dropdown><button class=dropdown-btn aria-haspopup=menu aria-label="Select the theme">
1+
<!doctype html><html lang=en-US><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Rust Programming Language Tutorials for Everyone!"><meta name=author content="Dumindu Madunuwan"><meta name=theme-color content="#ffffff" media="(prefers-color-scheme: light)"><meta name=theme-color content="#101010" media="(prefers-color-scheme: dark)"><title>Learning Rust · Rust Programming Language Tutorials for Everyone!</title><link rel=canonical href=https://learning-rust.github.io/><link rel=stylesheet href=/assets/css/home.min.2f64b534694f7783ee00c07c45f130ef260d59af33aefe9e41a6606a53c5de38.css integrity><link href=https://learning-rust.github.io/pagefind/pagefind-component-ui.css rel=stylesheet><link rel=manifest href=/manifest.json><link rel=icon href=/favicon/favicon.ico><link rel=icon href=/favicon/favicon-16x16.png sizes=16x16 type=image/png><link rel=icon href=/favicon/favicon-32x32.png sizes=32x32 type=image/png><link rel=apple-touch-icon href=/favicon/apple-touch-icon.png sizes=180x180><script async src="https://www.googletagmanager.com/gtag/js?id=G-FZHQCXSZ89"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag("js",new Date),gtag("config","G-FZHQCXSZ89")</script><meta property="og:title" content="Learning Rust"><meta property="og:description" content="Learning Rust - Rust Programming Language Tutorials for Everyone!"><meta property="og:type" content="article"><meta property="og:url" content="https://learning-rust.github.io/"><meta property="og:image" content="https://learning-rust.github.io/og.jpg"><meta property="og:site_name" content="Learning Rust"><meta name=twitter:card content="summary_large_image"><meta name=twitter:title content="Learning Rust"><meta name=twitter:description content="Learning Rust - Rust Programming Language Tutorials for Everyone!"><meta name=twitter:image content="https://learning-rust.github.io/og.jpg"><script type=application/ld+json>{"@context":"https://schema.org","@type":"Article","headline":"Learning Rust","description":"Learning Rust - Rust Programming Language Tutorials for Everyone!","image":"https:\/\/learning-rust.github.io\/og.jpg","author":{"@type":"Person","name":"Dumindu Madunuwan","url":"https:\/\/github.com\/dumindu"},"publisher":{"@type":"Organization","name":"Learning Rust","logo":{"@type":"ImageObject","url":"https:\/\/learning-rust.github.io\/"}},"dateModified":"2026-08-16T12:40:51Z","mainEntityOfPage":{"@type":"WebPage","@id":"https:\/\/learning-rust.github.io\/"}}</script></head><body><div id=content-wrapper><header id=site-header><div id=site-header-logo><a href=https://learning-rust.github.io/><img height=52px src=https://learning-rust.github.io/logo.svg alt="Site Logo"></a></div><div id=site-header-actions><div id=search-box><pagefind-modal-trigger compact=true></pagefind-modal-trigger><pagefind-modal></pagefind-modal></div><div id=theme-dropdown class=dropdown><button class=dropdown-btn aria-haspopup=menu aria-label="Select the theme">
22
<span><svg class="icon" height="20" viewBox="0 -960 960 960" width="20"><path d="M480-96q-79 0-149-30t-122.5-82.5T126-331 96-480q0-80 30.5-149.5t84-122 125-82.5T488-864q78 0 146.5 27T754-763t80.5 110T864-518q0 96-67 163t-163 67h-68q-8 0-14 5t-6 13q0 15 15 25t15 53q0 37-27 66.5T480-96zm0-384zm-173.5 18.5Q324-479 324-504t-17.5-42.5T264-564t-42.5 17.5T204-504t17.5 42.5T264-444t42.5-17.5zm120-144Q444-623 444-648t-17.5-42.5T384-708t-42.5 17.5T324-648t17.5 42.5T384-588t42.5-17.5zm192 0Q636-623 636-648t-17.5-42.5T576-708t-42.5 17.5T516-648t17.5 42.5T576-588t42.5-17.5zm120 144Q756-479 756-504t-17.5-42.5T696-564t-42.5 17.5T636-504t17.5 42.5T696-444t42.5-17.5zM480-168q11 0 17.5-8.5T504-192q0-16-15-28t-15-50 26.5-64 64.5-26h69q66 0 112-46t46-112q0-115-88.5-194.5T488-792q-134 0-227 91t-93 221 91 221 221 91z"/></svg>
33
</span><span><svg class="icon" height="20" viewBox="0 -960 960 960" width="20"><path d="m480-246 85.91-85.91Q577-343 591.5-343t25.5 11 11 25.5-10.93 25.43L505-169q-5.4 5-11.7 7.5t-13.5 2.5-13.5-2.5T455-169L342.93-281.07Q332-292 332.5-306.5T344-332t25.5-11 25.47 11.09L480-246zm0-468-85.91 85.91Q383-617 368.5-617T343-628t-11-25.5 10.93-25.43L455-791q5.4-5 11.7-7.5t13.5-2.5 13.5 2.5T505-791l112.07 112.07Q628-668 628-654t-11 25-25.5 11-25.59-10.97L480-714z"/></svg></span></button><ul role=menu class=dropdown-menu><li role=menuitem><button title=Light class=color-scheme data-value=light style=background:#fff;color:#000>
44
<span style=color:#000>Aa</span></button></li><li role=menuitem><button title=dark class=color-scheme data-value=dark style=background:#101010;color:#fff>

0 commit comments

Comments
 (0)