» Rust: Build a REST API with Rocket » 2. Development » 2.10 Pagination

Pagination

If you have millions of records in your database, you wouldn't want to display them all on one page. Doing so would be overwhelming for both the client and the server. That's why pagination is necessary.

Add pagination logic

Tune method parameters in domain/gateway/book_manager.rs:

@@ -7,5 +7,5 @@ pub trait BookManager: Send + Sync {
     fn update_book(&self, id: u32, b: &model::Book) -> Result<(), Box<dyn Error>>;
     fn delete_book(&self, id: u32) -> Result<(), Box<dyn Error>>;
     fn get_book(&self, id: u32) -> Result<Option<model::Book>, Box<dyn Error>>;
-    fn get_books(&self) -> Result<Vec<model::Book>, Box<dyn Error>>;
+    fn get_books(&self, offset: u32) -> Result<Vec<model::Book>, Box<dyn Error>>;
 }

Add a parameter named offset for the get_books method.

Add page_size config item in infrastructure/config/mod.rs:

@@ -25,6 +25,7 @@ pub struct CacheConfig {
 #[derive(Debug, Deserialize, Serialize)]
 pub struct ApplicationConfig {
     pub port: i32,
+    pub page_size: u32,
 }
 
 pub fn parse_config(file_name: &str) -> Config {

Setting a value for page_size in config.toml:

@@ -1,5 +1,6 @@
 [app]
 port = 8080
+page_size = 5
 
 [db]
 file_name = "test.db"

Wire in page_size in application/wire_helper.rs:

@@ -13,7 +13,8 @@ pub struct WireHelper {
 
 impl WireHelper {
     pub fn new(c: &Config) -> Result<Self, Box<dyn std::error::Error>> {
-        let sql_persistence = Arc::new(database::MySQLPersistence::new(&c.db.dsn)?);
+        let sql_persistence =
+            Arc::new(database::MySQLPersistence::new(&c.db.dsn, c.app.page_size)?);
         let no_sql_persistence = Arc::new(database::MongoPersistence::new(
             &c.db.mongo_uri,
             &c.db.mongo_db_name,

Update the query logic to include offset and page_size in infrastructure/database/mysql.rs:

@@ -9,12 +9,13 @@ use crate::domain::model;
 
 pub struct MySQLPersistence {
     pool: Pool,
+    page_size: u32,
 }
 
 impl MySQLPersistence {
-    pub fn new(dsn: &str) -> Result<Self, MySQLError> {
+    pub fn new(dsn: &str, page_size: u32) -> Result<Self, MySQLError> {
         let pool = Pool::new(dsn)?;
-        Ok(MySQLPersistence { pool })
+        Ok(MySQLPersistence { pool, page_size })
     }
 }
 
@@ -93,10 +94,10 @@ impl BookManager for MySQLPersistence {
         Ok(books.first().cloned())
     }
 
-    fn get_books(&self) -> Result<Vec<model::Book>, Box<dyn Error>> {
+    fn get_books(&self, offset: u32) -> Result<Vec<model::Book>, Box<dyn Error>> {
         let mut conn = self.pool.get_conn()?;
         let books = conn.query_map(
-            "SELECT * FROM books",
+            format!("SELECT * FROM books LIMIT {}, {}", offset, self.page_size),
             |(
                 id,
                 title,

Tune cache keys in application/executor/book_operator.rs:

@@ -30,15 +30,16 @@ impl BookOperator {
         self.book_manager.get_book(id)
     }
 
-    pub fn get_books(&self) -> Result<Vec<model::Book>, Box<dyn std::error::Error>> {
-        let raw_value = self.cache_helper.load(BOOKS_KEY)?;
+    pub fn get_books(&self, offset: u32) -> Result<Vec<model::Book>, Box<dyn std::error::Error>> {
+        let k = format!("{}-{}", BOOKS_KEY, offset);
+        let raw_value = self.cache_helper.load(&k)?;
         if let Some(v) = raw_value {
             let cached_books = serde_json::from_str(&v)?;
             Ok(cached_books)
         } else {
-            let fetched_books = self.book_manager.get_books()?;
+            let fetched_books = self.book_manager.get_books(offset)?;
             let v = serde_json::to_string(&fetched_books)?;
-            self.cache_helper.save(BOOKS_KEY, &v)?;
+            self.cache_helper.save(&k, &v)?;
             Ok(fetched_books)
         }
     }

Last step, pass in the query parameters in adapter/router.rs:

@@ -24,11 +24,12 @@ pub fn health_check() -> content::RawJson<&'static str> {
     content::RawJson("{\"status\":\"ok\"}")
 }
 
-#[get("/books")]
+#[get("/books?<o>")]
 pub fn get_books(
     rest_handler: &rocket::State<RestHandler>,
+    o: Option<u32>,
 ) -> Result<Json<Vec<model::Book>>, status::Custom<Json<ErrorResponse>>> {
-    match rest_handler.book_operator.get_books() {
+    match rest_handler.book_operator.get_books(o.unwrap_or(0)) {
         Ok(books) => Ok(Json(books)),
         Err(err) => Err(status::Custom(
             Status::InternalServerError,

Alright! Done with code changes. Let's try it out.

Try with curl

Put in some data for test

curl -X POST -H "Content-Type: application/json" -d '{"title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "published_at": "1925-04-10", "description": "A novel depicting the opulent lives of wealthy Long Island residents during the Jazz Age.", "isbn": "9780743273565", "total_pages": 218}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "To Kill a Mockingbird", "author": "Harper Lee", "published_at": "1960-07-11", "description": "A novel set in the American South during the 1930s, dealing with themes of racial injustice and moral growth.", "isbn": "9780061120084", "total_pages": 281}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "1984", "author": "George Orwell", "published_at": "1949-06-08", "description": "A dystopian novel depicting a totalitarian regime, surveillance, and propaganda.", "isbn": "9780451524935", "total_pages": 328}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "Pride and Prejudice", "author": "Jane Austen", "published_at": "1813-01-28", "description": "A classic novel exploring the themes of love, reputation, and social class in Georgian England.", "isbn": "9780486284736", "total_pages": 279}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "The Catcher in the Rye", "author": "J.D. Salinger", "published_at": "1951-07-16", "description": "A novel narrated by a disaffected teenager, exploring themes of alienation and identity.", "isbn": "9780316769488", "total_pages": 277}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "The Lord of the Rings", "author": "J.R.R. Tolkien", "published_at": "1954-07-29", "description": "A high fantasy epic following the quest to destroy the One Ring and defeat the Dark Lord Sauron.", "isbn": "9780544003415", "total_pages": 1178}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "Moby-Dick", "author": "Herman Melville", "published_at": "1851-10-18", "description": "A novel exploring themes of obsession, revenge, and the nature of good and evil.", "isbn": "9780142000083", "total_pages": 624}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "The Hobbit", "author": "J.R.R. Tolkien", "published_at": "1937-09-21", "description": "A fantasy novel set in Middle-earth, following the adventure of Bilbo Baggins and the quest for treasure.", "isbn": "9780345339683", "total_pages": 310}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "The Adventures of Huckleberry Finn", "author": "Mark Twain", "published_at": "1884-12-10", "description": "A novel depicting the journey of a young boy and an escaped slave along the Mississippi River.", "isbn": "9780486280615", "total_pages": 366}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "War and Peace", "author": "Leo Tolstoy", "published_at": "1869-01-01", "description": "A novel depicting the Napoleonic era in Russia, exploring themes of love, war, and historical determinism.", "isbn": "9781400079988", "total_pages": 1392}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "Alice’s Adventures in Wonderland", "author": "Lewis Carroll", "published_at": "1865-11-26", "description": "A children’s novel featuring a young girl named Alice who falls into a fantastical world populated by peculiar creatures.", "isbn": "9780141439761", "total_pages": 192}' http://localhost:8000/books
curl -X POST -H "Content-Type: application/json" -d '{"title": "The Odyssey", "author": "Homer", "published_at": "8th Century BC", "description": "An ancient Greek epic poem attributed to Homer, detailing the journey of Odysseus after the Trojan War.", "isbn": "9780140268867", "total_pages": 541}' http://localhost:8000/books

List books of first page

curl -X GET http://localhost:8000/books

Result:

[
  {
    "id": 3,
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200,
    "created_at": "2024-03-01 12:40:16",
    "updated_at": "2024-03-01 12:40:16"
  },
  {
    "id": 4,
    "title": "The Great Gatsby",
    "author": "F. Scott Fitzgerald",
    "published_at": "1925-04-10",
    "description": "A novel depicting the opulent lives of wealthy Long Island residents during the Jazz Age.",
    "isbn": "9780743273565",
    "total_pages": 218,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 5,
    "title": "To Kill a Mockingbird",
    "author": "Harper Lee",
    "published_at": "1960-07-11",
    "description": "A novel set in the American South during the 1930s, dealing with themes of racial injustice and moral growth.",
    "isbn": "9780061120084",
    "total_pages": 281,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 6,
    "title": "1984",
    "author": "George Orwell",
    "published_at": "1949-06-08",
    "description": "A dystopian novel depicting a totalitarian regime, surveillance, and propaganda.",
    "isbn": "9780451524935",
    "total_pages": 328,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 7,
    "title": "Pride and Prejudice",
    "author": "Jane Austen",
    "published_at": "1813-01-28",
    "description": "A classic novel exploring the themes of love, reputation, and social class in Georgian England.",
    "isbn": "9780486284736",
    "total_pages": 279,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  }
]

List books after an offset

curl -X GET "http://localhost:8000/books?o=5"

Result:

[
  {
    "id": 8,
    "title": "The Catcher in the Rye",
    "author": "J.D. Salinger",
    "published_at": "1951-07-16",
    "description": "A novel narrated by a disaffected teenager, exploring themes of alienation and identity.",
    "isbn": "9780316769488",
    "total_pages": 277,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 9,
    "title": "The Lord of the Rings",
    "author": "J.R.R. Tolkien",
    "published_at": "1954-07-29",
    "description": "A high fantasy epic following the quest to destroy the One Ring and defeat the Dark Lord Sauron.",
    "isbn": "9780544003415",
    "total_pages": 1178,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 10,
    "title": "Moby-Dick",
    "author": "Herman Melville",
    "published_at": "1851-10-18",
    "description": "A novel exploring themes of obsession, revenge, and the nature of good and evil.",
    "isbn": "9780142000083",
    "total_pages": 624,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 11,
    "title": "The Hobbit",
    "author": "J.R.R. Tolkien",
    "published_at": "1937-09-21",
    "description": "A fantasy novel set in Middle-earth, following the adventure of Bilbo Baggins and the quest for treasure.",
    "isbn": "9780345339683",
    "total_pages": 310,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 12,
    "title": "The Adventures of Huckleberry Finn",
    "author": "Mark Twain",
    "published_at": "1884-12-10",
    "description": "A novel depicting the journey of a young boy and an escaped slave along the Mississippi River.",
    "isbn": "9780486280615",
    "total_pages": 366,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  }
]

List books after another offset

curl -X GET "http://localhost:8000/books?o=10"

Result:

[
  {
    "id": 13,
    "title": "War and Peace",
    "author": "Leo Tolstoy",
    "published_at": "1869-01-01",
    "description": "A novel depicting the Napoleonic era in Russia, exploring themes of love, war, and historical determinism.",
    "isbn": "9781400079988",
    "total_pages": 1392,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 14,
    "title": "Alice’s Adventures in Wonderland",
    "author": "Lewis Carroll",
    "published_at": "1865-11-26",
    "description": "A children’s novel featuring a young girl named Alice who falls into a fantastical world populated by peculiar creatures.",
    "isbn": "9780141439761",
    "total_pages": 192,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 15,
    "title": "The Odyssey",
    "author": "Homer",
    "published_at": "8th Century BC",
    "description": "An ancient Greek epic poem attributed to Homer, detailing the journey of Odysseus after the Trojan War.",
    "isbn": "9780140268867",
    "total_pages": 541,
    "created_at": "2024-03-02 05:48:25",
    "updated_at": "2024-03-02 05:48:25"
  },
  {
    "id": 16,
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200,
    "created_at": "2024-03-07 10:17:59",
    "updated_at": "2024-03-07 10:17:59"
  },
  {
    "id": 17,
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200,
    "created_at": "2024-03-07 10:18:20",
    "updated_at": "2024-03-07 10:18:20"
  }
]

Looks like pagination is working! So far so good!

PrevNext