Thời lượng: 25 phút
Level: Intermediate
Yêu cầu: Đã hiểu về kỹ thuật phân trang.
Mục tiêu bài học
Sau bài học này, bạn sẽ:
- Thiết kế được cấu trúc tham số lọc (Filtering) nhất quán.
- Xây dựng được cơ chế sắp xếp (Sorting) đa dạng qua Query Params.
- Hiểu cách bảo mật API khi cho phép Client filter dữ liệu.
Nội dung chính
1. Filtering (Bộ lọc)
Có nhiều cách để truyền tham số lọc vào URL. Trong một Design System, chúng ta cần chọn một kiểu duy nhất.
Kiểu 1: Simple Filter (Khuyên dùng cho nhu cầu cơ bản)
GET /orders?status=completed&category=electronics
- ✅ Ưu điểm: Đơn giản, dễ đọc.
- ❌ Nhược điểm: Khó biểu diễn các phép toán phức tạp (Lớn hơn, Nhỏ hơn, Trong khoảng...).
Kiểu 2: Operator Filter (Dùng cho nhu cầu nâng cao)
GET /orders?price[gt]=100&created_at[after]=2024-01-01
- ✅ Ưu điểm: Rất mạnh mẽ. Hỗ trợ
gt(lớn hơn),lt(nhỏ hơn),in(trong danh sách). - ❌ Nhược điểm: URL trở nên phức tạp nếu không được document tốt.
2. Sorting (Sắp xếp)
Cách đặt tên tham số sắp xếp cũng cần được thống nhất.
Chuẩn đề xuất: sort và ký hiệu dấu trừ - để biểu thị giảm dần (descending).
GET /users?sort=created_at: Sắp xếp ngày tạo tăng dần.GET /users?sort=-created_at: Sắp xếp ngày tạo giảm dần.GET /users?sort=-created_at,username: Sắp xếp theo nhiều trường (Phân tách bằng dấu phẩy).
3. Bảo mật và Hiệu năng
Việc cho phép Client filter/sort tùy ý tiềm ẩn rủi ro:
- SQL Injection: Nếu không parse query params cẩn thận.
- Slow Query: Nếu client yêu cầu sắp xếp theo một trường chưa được đánh INDEX trong Database.
Nguyên tắc:
- Chỉ cho phép filter/sort trên các trường được định nghĩa trước (Allow-list).
- Mọi trường có thể
sortđều PHẢI có Index trong database.
Nguyên tắc then chốt
"Đừng bao giờ map trực tiếp query params vào câu lệnh Database."
Hãy luôn có một lớp trung gian (Mapper/Validator) để chuyển đổi từ tham số API (sort=-price) sang câu lệnh DB (ORDER BY price DESC). Điều này giúp bảo mật và cho phép bạn đổi tên trường trong DB mà không làm ảnh hưởng đến API contract.
Thực hành
Bài tập 1: Thiết kế bộ lọc cho Kho hàng (Inventory)
Bối cảnh: Bạn cần viết API lấy danh sách sản phẩm với các yêu cầu lọc:
- Giá từ 500 đến 1000.
- Trạng thái là 'còn hàng' (available).
- Sắp xếp theo ngày cập nhật mới nhất.
Yêu cầu:
- Hãy thiết kế URL query params hoàn chỉnh cho API này.
Xem đáp án mẫu
GET /products?price[gte]=500&price[lte]=1000&status=available&sort=-updated_at
(Trong đó gte là Greater Than or Equal, lte là Less Than or Equal)
Tình huống thực tế
Scenario 1: Lọc dữ liệu theo quan hệ
Bối cảnh:
Bạn muốn lấy danh sách Đơn hàng (Orders) của những người dùng ở thành phố "Hà Nội".
Vấn đề:
Thông tin thành phố nằm ở bảng Users, không nằm ở bảng Orders.
Giải quyết:
Chúng ta có thể dùng dấu chấm để biểu thị quan hệ lồng nhau trong filter:
GET /orders?user.city=hanoi
Backend sẽ phải thực hiện một phép JOIN trong database để xử lý filter này. Chú ý: Càng nhiều phép join lồng nhau, hiệu năng sẽ càng giảm. Đừng cho phép lồng quá 2 cấp (VD: user.profile.address.city là quá sâu).
Câu hỏi phỏng vấn
Senior Level
- Q: Làm thế nào để xử lý việc Filter trên hàng triệu bản ghi mà không làm sập Database?
A:
- Index: Đảm bảo mọi trường filter đều có index.
- Query Timeout: Thiết lập thời gian tối đa cho 1 request query.
- Pre-filtering: Luôn ép user có ít nhất một filter mặc định (VD: Theo TenantID hoặc Time range trong vòng 30 ngày).
- Search Engine: Với các bộ lọc quá phức tạp và đa dạng, hãy cân nhắc chuyển dữ liệu sang Elasticsearch/Meilisearch thay vì dùng quan hệ (PostgreSQL/MySQL).
Tóm tắt
Những điều cần nhớ
- Dùng
sortvới tiền tố-cho giảm dần. - Dùng
[operator]cho các phép toán so sánh. - Luôn dùng Allow-list để chặn các trường không được phép filter/sort.
Key Takeaways
- Predictability: Mọi List API trong hệ thống phải có cách dùng filter giống hệt nhau.
- Performance: Index là điều kiện tiên quyết để cho phép sorting.
Bước tiếp theo
Trong bài tiếp theo, bạn sẽ học về Lesson 5.3: Search Best Practices - Cách triển khai chức năng tìm kiếm từ khóa an toàn và hiệu quả.