Bài viết được sự cho phép của tác giả Tống Xuân Hoài
Vấn đề
Khi viết chương trình, chúng ta có các Design Pattern nên hoặc thậm chí cần được tuân theo để giúp cho mọi người trong nhóm có thể hiểu và phối hợp với nhau một cách nhịp nhàng. Các “mẫu thiết kế” là minh chứng rõ nhất cho việc đọc hiểu dễ hơn khi con người chúng ta cùng chung một lối suy nghĩ.
Design API cũng vậy, việc tạo ra hàng tá endpoint trong một khoảng thời gian là khá dễ nhưng làm sao để duy trì được sự thống nhất trong suốt quá trình phát triển mới là điều khó. Dự án phát triển theo năm tháng, con người cũng thay đổi, càng có nhiều bộ óc tham gia vào quá trình, càng có nhiều tính cách được bộc phát qua những dòng code.
Thực tê, Design API phụ thuộc vào tính sáng tạo của từng cá nhân, tổ chức. Nếu một nhóm không quá khắt khe với vấn đề đặt tên cho một endpoint thì thế giới này là của bạn, hãy tự do sáng tạo dựa trên những gì có thể nghĩ ra được. Ngược lại, có những tổ chức lại đưa ra những quy định ngặt nghèo trong quá trình này, buộc chúng ta phải tuân theo. Nhưng dù là thế nào đi nữa, mục đích cuối cùng của việc đó vẫn là làm sao để đại đa số lập trình viên nhìn vào đều có thể hiểu hoặc biết cách để tìm kiếm thứ mà họ cần.
Tôi là một người thích sự sáng tạo, nhưng không nên phá cách. Có những thứ mình vẫn cần tuân theo để đảm bảo được sự minh bạch của API. Bài viết này hôm nay, tôi sẽ trình bày một số quy tắc trong quá trình Design API mà rất nhiều lập trình viên trên thế giới đang áp dụng. Nếu không tin, sau khi đọc xong bài viết này, hãy thử “vọc vạch” một bộ API của bất kỳ bên thứ 3 nào đó đang cung cấp để xem họ đang làm như thế nào.
Một lần nữa, các quy tắc nêu ra dưới đây chỉ để tham khảo và không bắt buộc ai phải làm theo. Nếu bạn nhận ra được lợi ích từ việc tuân theo nguyên tắc, tôi tin rằng đây là một điều không nên bỏ qua. Không dài dòng nữa, vào việc thôi!
Một số quy tắc
Đầu tiên, hãy làm rõ một số khái niệm liên quan đến việc thiết kế API.
Chúng ta có resource, đại diện cho “tài nguyên” muốn truy cập hoặc thực hiện một thao tác. Khi gọi đến một endpoint, dữ liệu được trả về, nó là danh sách bài viết, chi tiết bài viết… gọi chung là resource. Bên cạnh đó, trong resource thường có các thuộc tính (attributes), chúng là một thành phần của resource hoặc được tính toán dựa trên resource.
Một “bộ sưu tập” hay Collections là tập hợp của các resource, thông thường các resource giống nhau được xếp chung vào một Collections. Ví dụ đối với một tài nguyên là “Users”, chúng ta có tập hợp các thao tác với người dùng như là thêm, sửa, xóa… gọi là Collections.
URLs thì rõ rồi, đường dẫn đến resource của bạn. Ví dụ /articles
để trỏ vào danh sách bài viết.
Xem thêm tuyển dụng Design Pattern hấp dẫn trên TopDev
Quy tắc về đường dẫn (path)
Xác định bộ sưu tập cho tài nguyên, ví dụ /articles
cho các bài viết, /comments
cho các bình luận và luôn sử dụng danh từ số nhiều.
Sử dụng “kebab-case” cho mọi thứ trong đường dẫn URLs, “camelCase” cho tham số trong truy vấn. Ví dụ /special-articles?isHighlight=true
.
Sử dụng “mã định danh” như ID của resource để thao tác với một tài nguyên chỉ định. Ví dụ: /articles/nodejs-la-gi
để tương tác với bài viết có URL “nodejs-la-gi”.
Tránh sử dụng đường dẫn có chứa thuộc tính của resource hoặc là các hành động đối với resource, nó sẽ gây ra nhiều rắc rối cũng như phức tạp hóa đường dẫn sau này. Ví dụ: không nên /articles/nodejs-la-gi/summary
hoặc /articles/nodejs-la-gi/reset-view
, hãy thử chuyển thuộc tính vào parameters hoặc trong request body, ví dụ /articles/nodejs-la-gi?summary=true
.
Nên xác định tiền tố cho phiên bản API, ví dụ /v1/articles
để trỏ đến phiên bản 1 của API. Nếu sau này nâng cấp API, chúng ta có thể mở rộng thêm /v2
, /v3
… mà hạn chế ảnh hưởng đến các phiên bản trước đó.
Quy tắc về phương thức
Có 5 phương thức HTTP phổ biến mà chắc hẳn ai cũng nghe qua.
GET: Để lấy một tài nguyên.
POST: Để tạo tài nguyên mới.
PUT: Để cập nhật các tài nguyên hiện có.
PATCH: Để cập nhật các tài nguyên hiện có, nhưng chỉ cập nhật các trường được cung cấp, để yên những trường khác.
DELETE: Để xóa các tài nguyên hiện có.
Kết hợp với quy tắc về đường dẫn, các phương thức HTTP góp phần làm rõ thêm hành động bạn đang muốn tương tác với tài nguyên là gì. Ví dụ:
GET /articles – lấy danh sách tất cả bài biết.
GET /articles/nodejs-la-gi – lấy chi tiết bài viết có URL nodejs-la-gi.
POST /articles – tạo mới một bài viết.
DELETE /articles – xóa bài viết.
…
Quy tắc về response status code
HTTP response status code là những con số cho biết liệu một yêu cầu HTTP có được hoàn thành hay là không.
Có rất nhiều status code nhưng dưới đây là một vài con số chúng ta thường hay gặp:
200 OK: thành công.
201 Created: thường được trả về khi API vừa tạo một tài nguyên mới thành công.
204 No Content: thao tác thành công nhưng không có gì được trả về, ví dụ như thao tác DELETE một tài nguyên đã có.
400 Bad Request: báo lỗi khi một yêu cầu không hợp lệ, có thể là hành vi gửi lên dữ liệu không hợp lệ cho nên cần phải từ chối xử lý.
401 Unauthorized: lỗi khi yêu cầu này cần xác thực thông tin hợp lệ trước khi hành động, ví dụ như chưa đăng nhập vào hệ thống.
403 Forbidden: lỗi khi không có quyền thực hiện chức năng này.
404 Not Found: lỗi không tìm thấy tài nguyên hoặc hành động.
500 Internal Server Error: một lỗi “không xác định” từ máy chủ, có thể do sự cố ngoài ý muốn như lỗi phần cứng, phần mềm, mạng…
Ngoài ra, bạn đọc có thể tham khảo thêm mã lỗi khác tại HTTP response status codes – Mozilla. Việc tuân thủ status code giúp đạt được sự thống nhất trong quá trình phát triển, cũng như cho các bên tích hợp API.
Quy tắc về response body
Giữ cho response body của bạn được xuyên suốt từ đầu đến cuối. Ví dụ một phản hồi thành công bao gồm mã phản hồi, thông điệp và dữ liệu trả về.
{
"status": 200,
"message": "OK",
"data": {
"id": 1,
"name": "John Doe",
"email": "johndoe@example.com"
}
}
Nếu là thông báo lỗi, hãy kèm theo mã lỗi và thông điệp lỗi.
{
"code": 400,
"message": "Bad Request"
}
Giữ thông báo lỗi chung nhất có thể để hạn chế việc làm lộ dữ liệu hoặc thông tin quan trọng khác.
Ngoài ra, có một vài quy tắc khác trong việc thiết kế API như:
Luôn phân trang cho các yêu cầu đến tài nguyên dạng danh sách, ví dụ /articles?limit=10&offset=0
hoặc /articles?page=1&limit=10
… Nếu có thể, trả về thêm tổng số resource có trong danh sách đó. Các tài nguyên dạng danh sách cũng có thể cần được sắp xếp theo một thứ tự nào đó, ví dụ /articles?sort=createdAt:desc
…
Tài nguyên chi tiết thông thường trả về hết thuộc tính có thể có, nhưng đó không phải lúc nào cũng cần, có thể hỗ trợ cần lấy những thuộc tính nào thông qua lọc, ví dụ /articles?field=url,title,content,createdAt
…
Bài viết gốc được đăng tải tại 2coffee.dev
Có thể bạn quan tâm:
- RESTful API là gì? Cách thiết kế RESTful API
- Top 5 API thú vị dành cho các New Developers
- Tạo RESTful API đơn giản bằng Nodejs + MongoDB
Tin tuyển dụng IT mọi cấp độ trên TopDev đang chờ bạn ứng tuyển!