Một API có thể chạy đúng nhưng vẫn là một API khó sử dụng.
Ví dụ:
GET /getAllUsers
POST /createNewUser
POST /deleteUser
Nhìn vào endpoint, frontend vẫn có thể gọi được. Nhưng khi hệ thống lớn lên, cách đặt tên và thiết kế như vậy dễ tạo ra sự thiếu nhất quán.
Một REST API tốt không chỉ trả về đúng dữ liệu. Nó cần có quy ước rõ ràng, dễ đoán và có khả năng mở rộng.
1. Thiết kế endpoint theo Resource, không theo Action
Một lỗi khá phổ biến là đặt endpoint giống như tên function:
GET /getUsers
POST /createUser
POST /deleteUser
POST /updateUser
Thay vào đó, hãy coi users là một resource:
GET /users
POST /users
GET /users/123
PATCH /users/123
DELETE /users/123
Ở đây:
GET /users→ lấy danh sách userPOST /users→ tạo userGET /users/123→ lấy user có ID 123PATCH /users/123→ cập nhật userDELETE /users/123→ xóa user
Điểm quan trọng là HTTP Method thể hiện hành động, còn URL thể hiện resource.
Thay vì:
POST /deleteUser
hãy dùng:
DELETE /users/123
Cách này giúp API dễ đoán hơn: chỉ cần biết resource và HTTP Method, developer có thể suy ra cách sử dụng endpoint.
2. Dùng HTTP Method đúng mục đích
Các Method thường gặp:
| Method | Mục đích |
|---|---|
GET | Lấy dữ liệu |
POST | Tạo resource hoặc thực hiện một operation phù hợp |
PUT | Thay thế toàn bộ resource |
PATCH | Cập nhật một phần resource |
DELETE | Xóa resource |
Ví dụ:
GET /products
→ Lấy danh sách sản phẩm.
POST /products
→ Tạo sản phẩm.
PATCH /products/123
→ Cập nhật một vài thông tin của sản phẩm 123.
DELETE /products/123
→ Xóa sản phẩm 123.
Không nên biến tất cả thao tác thành:
POST /products/update
POST /products/delete
POST /products/get
khi không có lý do đặc biệt.
3. Thiết kế Response nhất quán
Một API có thể có endpoint này trả:
{
"id": 123,
"name": "Laptop"
}
nhưng endpoint khác lại trả:
{
"success": true,
"data": {
"id": 456,
"name": "Mouse"
}
}
Nếu không có quy ước rõ ràng, frontend phải xử lý từng API theo một kiểu.
Một format nhất quán sẽ dễ sử dụng hơn.
Ví dụ:
{
"data": {
"id": 123,
"name": "Laptop"
}
}
Với danh sách:
{
"data": [
{
"id": 123,
"name": "Laptop"
},
{
"id": 124,
"name": "Mouse"
}
]
}
Tuy nhiên, không có một response format duy nhất bắt buộc cho mọi REST API.
Điều quan trọng hơn là:
API đã chọn format nào thì nên giữ tính nhất quán giữa các endpoint.
4. Sử dụng HTTP Status Code thay vì nhét mọi thứ vào 200
Ví dụ request lấy user không tồn tại:
GET /users/999
Không nên luôn trả:
200 OK
với:
{
"success": false,
"message": "User not found"
}
Có thể sử dụng:
404 Not Found
Tương tự:
200 → Request thành công
201 → Resource được tạo
204 → Thành công nhưng không có response body
400 → Request không hợp lệ
401 → Chưa xác thực
403 → Đã xác thực nhưng không có quyền
404 → Không tìm thấy resource
409 → Conflict
500 → Lỗi phía server
Điều này giúp frontend có thể xử lý lỗi dựa trên HTTP semantics thay vì phải đọc message để đoán chuyện gì xảy ra.
5. API cần được thiết kế để có thể mở rộng
Một API ban đầu có thể chỉ có:
GET /products
Nhưng khi dữ liệu tăng lên hàng trăm nghìn sản phẩm, việc trả toàn bộ dữ liệu sẽ không còn hợp lý.
Ngay từ đầu có thể thiết kế:
GET /products?page=1&limit=20
Hoặc:
GET /products?page=2&limit=20&sort=name
Response:
{
"data": [
{
"id": 101,
"name": "Laptop"
},
{
"id": 102,
"name": "Mouse"
}
],
"pagination": {
"page": 2,
"limit": 20,
"total": 150
}
}
Tương tự, API thường sẽ cần:
Filtering
GET /products?category=electronics
Sorting
GET /products?sort=price
Searching
GET /products?search=laptop
Pagination
GET /products?page=1&limit=20
Nhờ vậy, API không cần tạo thêm hàng loạt endpoint như:
GET /getProductsByCategory
GET /searchProducts
GET /getProductsSortedByPrice
Thay vào đó, cùng một resource có thể được mở rộng bằng query parameters.
6. Một số nguyên tắc giúp API dễ maintain hơn
Đặt tên nhất quán
Nên chọn một convention:
/users
/products
/stock-transactions
và duy trì nó.
Tránh:
/users
/productList
/get-stock-transactions
Không đưa implementation detail vào URL
Ví dụ:
/users/getFromMongoDB
không phải một thiết kế tốt.
Client chỉ cần biết resource:
/users
Database phía sau là MongoDB, PostgreSQL hay Oracle không nên trở thành một phần của API contract.
Version API khi cần
Khi có breaking change:
/api/v1/users
/api/v2/users
Versioning giúp client cũ có thời gian chuyển sang API mới thay vì bị phá vỡ ngay lập tức.
Tuy nhiên, versioning không nên được thêm một cách máy móc cho mọi thay đổi nhỏ. Điều quan trọng là xác định rõ API contract và khi nào thay đổi được xem là breaking change.
7. Một ví dụ API chưa tốt và cách cải thiện
❌ Thiết kế khó mở rộng
POST /getProducts
POST /createProduct
POST /updateProduct
POST /deleteProduct
POST /searchProducts
✅ Thiết kế theo Resource
GET /api/v1/products
POST /api/v1/products
GET /api/v1/products/123
PATCH /api/v1/products/123
DELETE /api/v1/products/123
Tìm kiếm:
GET /api/v1/products?search=laptop
Phân trang:
GET /api/v1/products?page=1&limit=20
Lọc:
GET /api/v1/products?category=electronics
Khi nhìn vào API này, developer có thể nhanh chóng đoán được cách sử dụng mà không cần đọc tên từng function bên backend.
8. Những lỗi thường gặp khi thiết kế REST API
1. Dùng URL để mô tả action
POST /createUser
POST /deleteUser
→ Nên tận dụng HTTP Method.
2. Không nhất quán response
Endpoint trả:
{"data": ...}
endpoint khác lại trả:
{"result": ...}
→ Chọn một convention và duy trì.
3. Trả 200 OK cho mọi trường hợp
→ Sử dụng HTTP Status Code phù hợp.
4. Tạo quá nhiều endpoint
Ví dụ mỗi kiểu filter lại tạo một endpoint riêng.
→ Tận dụng query parameters khi phù hợp.
5. Không nghĩ đến pagination
GET /products
trả về hàng triệu record.
→ Thiết kế pagination ngay khi resource có khả năng tăng lớn.
Tóm lại
Một REST API tốt không phải là API có thật nhiều endpoint.
Hãy cố gắng đạt được 4 điều:
Resource rõ ràng
↓
HTTP Method đúng
↓
Response & Status Code nhất quán
↓
Có khả năng mở rộng
Mục tiêu cuối cùng là:
Developer mới nhìn vào API cũng có thể đoán được cách sử dụng mà không phải đọc toàn bộ source code backend.


