Chào các bạn, hiện tại do đang làm dự án về viết API cho app nên mình có cơ hội tìm hiểu được sâu về các kiến trúc cũng như các quy ước tên( naming convensions) khi thiết kế  1 RESTful API. Hôm nay mình xin mạn phép chia sẻ về những rules khi thiết kế 1 API "chuẩn" mà bạn nên follow theo. Bài viết dựa trên các bài viết mình đã học hỏi được và một phần từ kinh nghiệm cá nhân.

I.  Khái quát về các loại Resource

Các điểm như khái niệm, các thành phần, cách hoạt động... mình sẽ không nói trong bài blog này do đã có quá nhiều bài viết viết về những phần này. Mình sẽ chỉ chú trọng về các quy chuẩn trong thiết kế API.

Trong thiết kế REST API, việc đặt tên đại diện cho các nguồn data mà chúng ta sử dụng (hay còn gọi là Resouces) thực sự rất rất quan trọng. Việc đặt tên chuẩn và clear nhất sẽ có tác dụng lâu dài trong việc thiết kế đối với các project mà độ scalable cực kì lớn.

  • Một Resource có thể là một chủ thể đơn lẻ (singleton)hoặc có thể là một tập hợp (collection). Ví dụ cụ thể cho dễ hiểu. Trong domain của 1 ngân hàng chẳng hạn, customer là một singleton resource, customers là một collection resource. Đối với collection resource trên, chúng ta có thể nhận định nó bằng URI " /customers ". Còn đối với singleton resource, chúng ta sử dụng /customers/{customerId}.
  • Một Resource cũng có thể chứa nhiều các tập hợp khác (sub-collection). Chẳng hạn trong ngân hàng, một chủ thể customer nắm giữ thông tin của các tài khoản accounts khác nhau. Chúng ta có thể định danh API này bằng URN: /customers/{customerId}/accounts để lấy toàn bộ thông tin sub-collection tài khoản của 1 user bất kì. Tương tự nếu muốn lấy 1 singleton resource account của accounts : /customers/{customerId}/accounts/{accountId} :D

Các URI nên được thiết kế, đặt tên liên quan hoặc trực tiếp trỏ tới model mà cần thiết đối với bên phía client sử dụng API. Việc này giúp cho người sử dụng API nhiều khi chỉ cần nhìn URI cũng có thể biết được chức năng của API.

Phần tiếp theo mình sẽ trình bày một số tips về việc đặt tên các URI mà mình thấy rất hữu ích.

II. Một vài lời khuyên đặt tên cho URI

1. Sử dụng danh từ để đại diện cho Resource.

RESTful API nên liên quan tới resource là một danh từ chỉ đối tượng thay vì một động từ chỉ hành động vì danh từ thường có các thuộc tính (properties) mà đông từ không có, cũng giống như resource thì thường có các attributes đi kèm. Có thể lấy ví dụ:

  • Một hệ thống với các máy móc.
  • Một hệ thống quản lý người dùng với các dữ liệu về các người dùng và thông tin cụ thể người dùng đó.
http://api.example.com/api/device-management/managed-devices 
http://api.example.com/api/device-management/managed-devices/{device-id} 
http://api.example.com/api/user-management/users/
http://api.example.com/api/user-management/users/{id}

Đi sâu hơn về phần này, chúng ta sẽ chia các API ra thành 4 nhóm: document, collection, store và controller. Tuỳ vào mục đích sử dụng, chúng ta chia các api ra các nhóm để sử dụng convension sao cho phù hợp nhất.

a. Document

Là resource chỉ các đối tượng độc lập, hay 1 bản ghi trong database. Nó là 1 singleton resource bên trong collection resource. Sử dụng danh từ số ít hoặc định danh của resource cho loại này.

http://api.example.com/device-management/managed-devices/{device-id}
http://api.example.com/user-management/users/{id}
http://api.example.com/user-management/users/admin
b. Collection

Là các resource được quản lý bởi server. Client có thể yêu cầu thêm các resource mới vào collection. Tuy nhiên, việc có được thêm hay không lại phụ thuộc vào bên thứ 3 (admin hoặc chính collection đó quy định).

Cho dễ hiểu, giống như bạn muốn đăng một Post lên một blog nào đó thì phải đợi admin của page đó phê duyệt. Hoặc ví dụ post của bạn về chính trị mà blog đó lại về công nghệ chẳng hạn, thì sẽ bị reject ngay lập tức.

Sử dụng các tên số nhiều với type này.

http://api.example.com/api/device-management/managed-devices
http://api.example.com/api/user-management/users
http://api.example.com/api/user-management/users/{id}/accounts
c. Store

Là các resource được quản lý bởi client - tức là người sử dụng API đó. Client có toàn quyền CRUD với resource này. Loại resource này chỉ nên có 1 URI, và không nên tạo ra các URI khác.

Với các type này, chúng ta sử dụng các tên số nhiều.

http://api.example.com/api/song-management/users/{id}/playlists
d. Controller

Loại resoure này như đại diện cho 1 hành động, 1 quá trình và có input-output, hoặc 1 giá trị.

Với các type này, hơi đặc biệt 1 chút. chúng ta nên sử dụng động từ để dễ hình dung.

http://api.example.com/api/auth/login
http://api.example.com/api/send-mail

2. Nhất quán URI theo một chuẩn chung

Như mình đã nói, chuẩn ở đây là để dễ đọc, dễ bảo trì. Dưới đây là một số quy tắc chung được cộng đồng khuyên dùng:

a. Sử dụng "/" để ngăn cách quan hệ trong resource
http://api.example.com/api/song-management/users/{id}/playlists

Như ví dụ này chả hạn. Ta có thể dễ dàng hiểu là đối với hệ thống, mỗi user sẽ có 1 playlist nhạc riêng.

b. Không sử dụng "/" cuối API
http://api.example.com/api/auth/login/ ->không nên
http://api.example.com/api/auth/login -> nên
c.  Sử dụng "-"

Để tăng khả năng đọc liền mạch đối với các path dài. Ví dụ:

http://api.example.com/api/messenger-management/group-chat/{id}/download-archived-conversation -> nên
http://api.example.com/api/messengerManagement/groupChat/{id}/downloadArchivedConversation -> không nên
d. Không sử dụng "_"

Bạn cũng có thể sử dụng "_" thay cho "-" để ngăn cách các segment. Tuy nhiên với 1 số các ứng dụng sử dụng các custom font, dấu gạch dưới sẽ rất khó hoặc hoàn toàn không nhìn thấy. Để tăng khả năng đọc, chúng ta hãy thống nhất dùng "-" nhé.

http://api.example.com/api/messenger-management/group-chat/{id}/download-archived-conversation -> nên
http://api.example.com/api/messenger_management/group_chat/{id}/download_archived_conversation -> không nên
e. Sử dụng chữ cái thường

Do RFC 3986 quy định, trừ scheme và host, path của URI sẽ phải check các case liên quan đến chữ viết hoa và viết thường.

http://api.example.org/my-folder/my-doc  //1 -> giống với 2
HTTP://API.EXAMPLE.ORG/my-folder/my-doc  //2 -> giống với 1
http://api.example.org/My-Folder/my-doc  //3 -> khác 1 và 2 do path có chữ viết hoa

Vì vậy để thuận tiện, chúng ta nên sử dụng hoàn toàn chữ cái thường.

f. Không sử dụng định dạng file

Việc sử dụng định dạng file trên URI không có tác dụng gì và trông rất tệ. Nếu muốn truyền file, bạn nên giao tiếp qua API thông qua body của request với Content-Type header.

http://api.example.com/api/messenger-management/group-chat/{id}/download-archived-conversation -> nên
http://api.example.com/api/messenger-management/group-chat/{id}/download-archived-conversation.json -> không nên

3. Không phân định CRUD trên URI

Vì vấn đề bảo mật, URI không nên được khai báo để cho biết rằng hành động CRUD đang được gọi.

Thay vào đó hãy sử dụng nó thông qua các phương thức HTTP:

// Không nên
http://api.example.com/user-management/users/get
http://api.example.com/user-management/users/create
http://api.example.com/user-management/users/{id}/get
http://api.example.com/user-management/users/get
http://api.example.com/user-management/users/{id}

// Nên
HTTP GET http://api.example.com/user-management/users -> get tất cả user
HTTP POST http://api.example.com/user-management/users -> tạo user mới
HTTP GET http://api.example.com/user-management/users/{id} -> get 1 user cụ thể
HTTP PUT http://api.example.com/user-management/users/{id} -> sửa 1 user cụ thể
HTTP DELETE http://api.example.com/user-management/users/{id} -> xoá 1 user cụ thể

4. Sử dụng query để filter các URI collection

Giả sử chúng ta có API để lấy tất cả các User. Nhưng rồi lại có thêm 1 requirement như trả về các user theo từng trang với số lượng nhất định. Hoặc ví dụ lọc ra các người dùng với quốc tịch là Việt Nam chẳng hạn. Thay vì viết API mới, mất rất nhiều thời gian. Chúng ta nên sử dụng các API cũ với thêm các query truyền vào

http://api.example.com/user-management/users?page=1&size=10
http://api.example.com/user-management/users?nationality=Vietnam

III. Kết luận

Trên đây mình đã trình bày về các quy chuẩn đặt tên khi thiết kế API.

Hy vọng nó có ích trong việc thiết kế một hệ thống API tối ưu cho dự án của bạn. :D