Mục lục

Lesson 5.1: Pagination Standards

Học cách thiết kế các cơ chế phân trang (Pagination) hiệu quả và nhất quán cho API.

Pagination
Performance
API Design

Thời lượng: 20 phút
Level: Intermediate
Yêu cầu: Đã nắm vững về Response Envelope.

Mục tiêu bài học

Sau bài học này, bạn sẽ:

  • Phân biệt được Offset-based và Cursor-based pagination.
  • Thiết kế được Metadata trả về cho Client xử lý giao diện phân trang.
  • Biết cách thiết lập các giá trị mặc định (Defaults) và giới hạn (Limits) an toàn.

Nội dung chính

1. Tại sao phải phân trang?

Một bảng dữ liệu có thể chứa hàng triệu bản ghi. Nếu API trả về toàn bộ:

  • Server sẽ quá tải khi query DB.
  • Băng thông bị lãng phí.
  • Client (Mobile/Web) sẽ bị đơ khi render hàng nghìn phần tử.

2. Hai kỹ thuật phổ biến

A. Offset-based Pagination (Dùng cho dữ liệu nhỏ/vừa)

Dùng cặp tham số: pagelimit (hoặc offsetlimit).

Ví dụ: GET /products?page=2&limit=20

  • Ưu điểm: Dễ hiểu, Client có thể nhảy đến trang bất kỳ (VD: trang 10).
  • Nhược điểm: Hiệu năng giảm dần ở các trang cuối (vì DB vẫn phải quét qua các bản ghi trước đó). Gặp vấn đề "Dữ liệu bị trôi" (Data drift) nếu có bản ghi mới được thêm/xóa trong lúc đang phân trang.

B. Cursor-based Pagination (Dùng cho dữ liệu lớn/Social Feed)

Dùng tham số: after (hoặc before) và limit.

Ví dụ: GET /posts?after=ID_CUA_BAI_CUOI_CUNG&limit=20

  • Ưu điểm: Hiệu năng cực nhanh (vì query trực tiếp từ ID/Index). Không bị lặp hoặc sót dữ liệu khi có bài mới chèn vào.
  • Nhược điểm: Không thể "nhảy cóc" đến một trang bất kỳ. Chỉ phù hợp cho cuộn vô tận (Infinite Scroll).

3. Metadata chuẩn cho Client

Trong Response Envelope, phần meta phải cung cấp đủ thông tin cho Client vẽ thanh phân trang.

Offset-based Response:

json:
{
  "success": true,
  "data": [...],
  "meta": {
    "page": 2,
    "limit": 20,
    "total_items": 1050,
    "total_pages": 53,
    "has_next": true,
    "has_previous": true
  }
}

Nguyên tắc then chốt

"Luôn có giá trị mặc định cho limit và thiết lập max_limit để tránh bị tấn công DDOS thông qua việc query dữ liệu quá lớn."

Best Practice:

  • Default limit: 20
  • Max limit: 100 (Nếu Client yêu cầu 1000, Server tự động ép về 100).

Thực hành

Bài tập 1: Thiết kế Metadata cho Cursor Pagination

Bối cảnh: Bạn đang xây dựng ứng dụng giống TikTok, dùng Cursor-based pagination.

Yêu cầu:

  • Hãy thiết kế các trường thông tin cần có trong phần meta để Client biết khi nào cần gọi tiếp API.
Xem đáp án mẫu
json:
{
  "meta": {
    "limit": 20,
    "next_cursor": "video_id_789",
    "has_more": true
  }
}

Tình huống thực tế

Scenario 1: Người dùng muốn nhảy đến trang cuối

Bối cảnh: Admin muốn xem những đơn hàng cũ nhất từ 3 năm trước trong bảng có 5 triệu đơn hàng.

Câu hỏi:

  1. Bạn nên dùng Offset hay Cursor?
  2. Làm sao để tối ưu hiệu năng trong trường hợp này?
Xem phân tích

Phân tích:

  • Admin cần nhảy trang -> Offset-based thuận tiện hơn cho UI.
  • Để tối ưu: Thay vì nhảy đến trang cuối của danh sách hiện tại (mất nhiều thời gian), hãy cho phép Admin đổi thuộc tính sắp xếp thành sort=created_at:asc (Tăng dần). Khi đó Admin sẽ lấy được đơn hàng cũ nhất ngay tại Trang 1. Đây là một mẹo thiết kế API giúp giảm áp lực cho Database.

Câu hỏi phỏng vấn

Mid Level

  1. Q: "Data Drift" (Dữ liệu bị trôi) trong phân trang offset là gì? A: Giả sử trang 1 trả về 10 item. Trong khi user đang xem, có 1 item mới được thêm vào đầu bảng. Khi user nhấn sang trang 2, Database sẽ lấy từ vị trí thứ 11. Nhưng vị trí thứ 11 lúc này chính là item thứ 10 cũ (vừa bị đẩy xuống). Kết quả: User thấy item thứ 10 bị lặp lại ở cả trang 1 và trang 2.

Tóm tắt

Những điều cần nhớ

  • Offset-based: Phù hợp cho Web, Dashboard, cần nhảy trang.
  • Cursor-based: Phù hợp cho Mobile, App mạng xã hội, dữ liệu cực lớn.
  • Luôn giới hạn max_limit để bảo vệ server.

Bước tiếp theo

Trong bài tiếp theo, bạn sẽ học về Lesson 5.2: Filtering & Sorting - Cách thiết kế bộ lọc linh hoạt mà không làm rối cấu trúc URL.

Tiếp tục bài 5.2 →

Quảng cáo
mdhorizontal