Appearance
Phân trang, lọc, sắp xếp
Khi 1 endpoint trả về danh sách (collection), gần như luôn cần 3 cơ chế này để tránh trả về toàn bộ dữ liệu cùng lúc.
Phân trang (pagination)
| Kiểu | Cách hoạt động | Ưu / nhược |
|---|---|---|
| Offset-based | GET /users?offset=20&limit=10 — bỏ qua 20 item đầu, lấy 10 item tiếp | Dễ hiểu, dễ implement, nhưng chậm dần khi offset lớn (database vẫn phải quét qua các dòng bị bỏ), và dễ bị lệch trang nếu dữ liệu thay đổi giữa các lần gọi |
| Cursor-based | GET /users?after=eyJpZCI6MTIzfQ&limit=10 — dùng 1 "con trỏ" (thường mã hoá từ id/thời điểm của item cuối cùng đã lấy) để lấy tiếp từ đó | Hiệu năng ổn định dù dữ liệu lớn, không bị lệch trang khi có thêm dữ liệu mới, nhưng không nhảy thẳng tới "trang số N" được (chỉ đi tiếp/lùi tuần tự) |
Khuyến nghị: dùng offset-based cho dữ liệu nhỏ/ổn định (ít thay đổi); dùng cursor-based cho dữ liệu lớn hoặc thay đổi liên tục (feed, log).
Response nên trả về gì
Không chỉ request cần tham số phân trang, response cũng nên kèm thông tin để client biết còn dữ liệu tiếp theo không — tránh việc client phải tự đoán hoặc gọi thừa 1 lần để kiểm tra:
json
{
"data": [ ... ],
"pagination": {
"page": 2,
"limit": 50,
"total": 1234,
"totalPages": 25,
"hasNext": true,
"hasPrev": true
}
}Với cursor-based, thay page/totalPages (không có khái niệm "trang số mấy") bằng cursor cho lần gọi tiếp theo, ví dụ "nextCursor": "eyJpZCI6MTIzfQ".
Lọc (filtering)
Dùng query parameter để client chỉ lấy đúng dữ liệu cần, ví dụ GET /orders?status=paid&created_after=2026-08-01. Tên tham số nên đặt theo đúng tên field dữ liệu, tránh viết tắt khó đoán.
Sắp xếp (sorting)
Quy ước phổ biến: GET /products?sort=price&order=asc (hoặc gộp thành sort=-price để biểu diễn giảm dần bằng dấu -). Cần tài liệu hoá rõ field nào được phép sort để tránh client truyền field không có index, gây chậm truy vấn.