[API]: Searching, sorting, filtering, pagination and version

Image for post
Image for post

Series: Designing RESTful APIs

Filtering / Searching

Tình huống đơn giản nhất là:

GET /items?seller_id=69&status=active

Câu lệnh có ý nghĩa là lấy ra những item có trạng thái active ứng với seller có id là 69.

Vậy nếu tình huống là:
Khoảng giá trị của giá sản phẩm, hoặc khoảng thời gian thì sẽ dư nào? Vì param của url chỉ cho phép cú pháp là key=value, vậy nếu ta muốn key, value và cả toán tử (=, >, <) thì sao?
Để giải quyết vấn đề này thì có khá nhiều cách.

Sử dụng dấu [] để thể hiện các phép so sánh(eq(equal), lte(less than or equal), gte(greater than or equal to)). ví dụ:

GET /items?price[gte]=10&price[lte]=100

Ở ví dụ trên ta đang tìm các item có giá trong khoảng từ lớn hơn hoặc bằng 10 nhưng phải nhỏ hơn hoặc bằng 100.

Chúng ta có nhiều toán tử khác nhau như: [lte], [gte], [eq], [exists], [regex], [before], và [after].

Ưu điểm:
- Dễ cho phía client khi gọi lên
- Dễ group param bởi tham số trên sẽ xác định là mảng của price
- Khi dùng các toán tử sử dụng string, ta không cần parse các kí tự đặc biệt

Nhược điểm:
- Hơi khó khăn trong việc parse trên server
- Với các chuỗi string ở trên thay toán tử thì cần phải convert về toán tử khi query trong database
- Khó khăn trong việc quản lý khi custom(cần thống nhất ở các API khác)
- Trong tình huống này ta đang ứng dụng trong trường hợp AND các filter, sẽ khó hơn trong việc ta sử dụng OR các filter với nhau.

Không biết gọi tên là gì, nhưng sử dụng kí tự “:”. Lấy ví dụ luôn cho nóng:

GET /items?price=gte:10&price=lte:100

Ý nghĩa thì cũng tương tự như ở cách 1, chỉ khác ở cách chúng ta thể hiện param trên url khác nhau mà thôi.

Tùy vào việc cách bạn xử lý ở phía backend thế nào thì sẽ chọn 1 cách nào đó tốt nhất và phù hợp nhất cho team.

Pagination

Phân trang là điều thực sự cần thiết và không phải bàn cãi khi dữ liệu cần GET quá lớn trong khi chúng ta cũng không thực sự muốn lấy full data như vậy (Có thể sẽ chỉ sử dụng trong trường hợp export data ra excel chẳng hạn, nhưng cũng sẽ có limit nào đó), vì điều này ảnh hưởng tới performance.

Phân trang để biết rằng dữ liệu ta lấy đang nằm ở thứ tự nào. Để thực hiện được điều này ta có cách sau:

GET /items?limit=10&offset=50

Câu lệnh trên mô tả dữ liệu được lấy ra 10 bản ghi và bắt đầu từ 50.

Trong mysql có sẵn câu lệnh LIMIT và OFFSET để ta lấy ra dữ liệu. Ở params trên ta có thể sử dụng cặp page và per_page để thay thế cho offset và limit.

Sẽ có những lúc bạn cần đến trường hợp lấy ra 20 bản ghi mới nhất, hoặc lấy ra 20 bản ghi mà trước ngày 24/02/2019. Nếu vậy thì làm thế nào?

Sẽ lấy ví dụ ngay đây:

GET /items?limit=20&created_at=desc
GET /items?limit=20&sort_by=-created_at
GET /items?limit=20&created_at[lte]=2019-02-24

Đó là một số giải pháp nho nhỏ mà nó sẽ giải quyết được phần nào vấn đề trên.

Chúng ta luôn phải kết hợp các param với nhau, trong trường hợp này là vừa search và filter.
Chẳng hạn ta muốn lấy ra 10 bản ghi với điều kiện là phải sau id thứ 20 và được sắp xếp theo email. Ví dụ:

GET /items?limit=10&after_id=20&sort_by=email

Về logic thì sẽ càng phức tạp hơn khi chúng ta thêm nhiều sự kết hợp giữa các param, kể cả thứ tự param cũng sẽ có ảnh hưởng tới việc xử lý ở phía server khá nhiều.

Sorting

Giống với bộ lọc và phân trang. Việc sắp xếp là vô cùng quan trọng trong thiết kế API. Một danh sách người dùng bạn muốn lấy ra theo thứ tự của thời gian active, sắp xếp theo thứ tự sửa đổi thông tin mới nhất…

Về nhu cầu thì khá nhiều trong việc sắp xếp, một API tốt sẽ cho phép chúng ta kết hợp sắp xếp với các tiêu chí khác nhau. Tăng dần(ascending-asc) hoặc giảm dần(descending-desc). Chính vì kết hợp nhiều column để sắp xếp nên thứ tự sắp xếp các column sẽ rất quan trọng. Ví dụ:

GET /users?sort_by=+email,-last_modified 
# sẽ khác với
GET /users?sort_by=-last_modified,+email

Một số cách khác để thể hiện param:

GET /users?sort_by=asc(email) và GET /users?sort_by=desc(email)GET /users?sort_by=+email và GET /users?sort_by=-emailGET /users?sort_by=email.asc và GET /users?sort_by=email.descGET /users?sort_by=email&order_by=asc và GET /users?sort_by=email&order_by=desc

Version

Phần này thường sẽ có 2 cách chính:
1. Thể hiện version qua URL luôn.

Ví dụ:

http://domain.com/api/v1/*

v1 ở đây thể hiện phiên bản đang sử dụng. Cách này được dùng rất phổ biến hiện nay.
2. Đưa thông số về version lên header.

Ví dụ:

`X-API-VERSION: 1.0`

Thông số trên được gửi kèm trong request lên, đây là phương pháp cũng vẫn hay được sử dụng(đặc biệt có Facebook).
Trong 2 phần trên thì recommend sử dụng cách 1 vì tính tiện lợi của nó.

Conclusion

Thiết kế API tốt là một thành phần quan trọng cho trải nghiệm của Developer. Thông số kỹ thuật API có thể tồn tại lâu hơn đòi hỏi phải suy nghĩ về các trường hợp sử dụng trong tương lai cho API, các cách sử dụng phải nhất quán thành convention của cả team.

Cảm ơn các bạn đã quan tâm bài viết . Có bất cứ phản hồi góp ý gì thì các bạn cứ cmt ở dưới nhé 😬

Written by

Be Curious!| ☕️+✍🏼=❤️ | buihuycuong.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store