Sử dụng JSON trong REST API: Các phương pháp tốt nhất
JSON trong REST API
JSON và REST API là một sự kết hợp hoàn hảo. Cú pháp nhẹ, hỗ trợ array tự nhiên và khả năng hỗ trợ ngôn ngữ phổ quát của JSON khiến nó trở thành định dạng mặc định cho các web API hiện đại. Nhưng chỉ sử dụng JSON trong API của bạn là chưa đủ -- việc tuân theo các quy ước và phương pháp tốt nhất đã được thiết lập đảm bảo API của bạn nhất quán, trực quan và dễ tích hợp.
Định dạng Request và Response
Cấu trúc Envelope nhất quán
Áp dụng một envelope JSON nhất quán cho tất cả các phản hồi API. Điều này giúp cho việc xử lý phía máy khách có thể dự đoán được và đơn giản hóa việc xử lý lỗi.
{
"status": "success",
"data": {
"user": {
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
},
"meta": {
"requestId": "req-a1b2c3d4",
"timestamp": "2026-07-19T10:30:00Z"
}
}
Snake Case vs Camel Case
Chọn một quy ước đặt tên và tuân thủ nó trên toàn bộ API của bạn:
- Camel Case (
createdAt,firstName): Phổ biến trong hệ sinh thái JavaScript/TypeScript - Snake Case (
created_at,first_name): Phổ biến trong hệ sinh thái Python, Ruby và PHP
Dù bạn chọn cách nào, hãy ghi lại nó rõ ràng và cân nhắc sử dụng một lớp chuyển đổi nếu ngôn ngữ backend của bạn sử dụng quy ước khác.
Mã trạng thái HTTP với JSON
Mã trạng thái HTTP phù hợp bổ sung cho các phản hồi JSON của bạn. Sử dụng chúng một cách nhất quán:
| Mã trạng thái | Ý nghĩa | Khi nào sử dụng |
|---------------|---------|-----------------|
| 200 OK | Thành công | GET, PUT, PATCH thành công |
| 201 Created | Tài nguyên đã được tạo | POST thành công |
| 204 No Content | Xóa thành công | DELETE thành công (không có body JSON) |
| 400 Bad Request | Cú pháp JSON không hợp lệ | Body yêu cầu không đúng định dạng |
| 401 Unauthorized | Yêu cầu xác thực | Thiếu hoặc token không hợp lệ |
| 403 Forbidden | Không đủ quyền | Xác thực hợp lệ nhưng không được phép |
| 404 Not Found | Tài nguyên không tồn tại | ID hoặc đường dẫn không hợp lệ |
| 422 Unprocessable Entity | Lỗi xác thực | Lỗi ngữ nghĩa trong JSON hợp lệ |
| 429 Too Many Requests | Đạt giới hạn tốc độ | Máy khách vượt quá giới hạn |
| 500 Internal Server Error | Lỗi phía máy chủ | Lỗi không mong muốn |
Định dạng phản hồi lỗi chuẩn
{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "The request body contains invalid fields",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"code": "INVALID_FORMAT"
},
{
"field": "age",
"message": "Must be a positive integer",
"code": "OUT_OF_RANGE"
}
]
}
}
Tài nguyên lồng nhau
Biểu diễn mối quan hệ
REST API thường xuyên cần biểu diễn các tài nguyên liên quan. Dưới đây là các mẫu phổ biến:
Nhúng (eager loading):
{
"order": {
"id": 5001,
"total": 29.99,
"customer": {
"id": 123,
"name": "Alice",
"email": "alice@example.com"
},
"items": [
{
"productId": 42,
"name": "Widget",
"quantity": 2,
"price": 14.99
}
]
}
}
Tham chiếu (lazy loading) -- sử dụng ID và cung cấp các endpoint riêng biệt:
{
"order": {
"id": 5001,
"total": 29.99,
"customerId": 123,
"itemIds": [101, 102]
}
}
Quy tắc chung: Nhúng dữ liệu liên quan luôn cần cùng nhau. Tham chiếu dữ liệu được truy xuất có điều kiện hoặc vào thời điểm khác.
Các mẫu phân trang
Khi trả về danh sách tài nguyên, phân trang là điều cần thiết. Sử dụng định dạng phân trang nhất quán:
Phân trang dựa trên Offset
{
"status": "success",
"data": [
{ "id": 1, "name": "User 1" },
{ "id": 2, "name": "User 2" }
],
"pagination": {
"page": 1,
"perPage": 20,
"totalItems": 156,
"totalPages": 8,
"links": {
"first": "/api/users?page=1&perPage=20",
"prev": null,
"next": "/api/users?page=2&perPage=20",
"last": "/api/users?page=8&perPage=20"
}
}
}
Phân trang dựa trên Cursor (Khuyến nghị cho tập dữ liệu lớn)
{
"status": "success",
"data": [
{ "id": 100, "name": "User 100" },
{ "id": 101, "name": "User 101" }
],
"pagination": {
"nextCursor": "eyJpZCI6MTAxfQ==",
"hasMore": true
}
}
Phân trang dựa trên cursor đáng tin cậy hơn cho dữ liệu thời gian thực nơi các bản ghi mới có thể làm thay đổi ranh giới trang.
JSON trong API Request
Body của POST / PUT Request
Chấp nhận JSON với header Content-Type: application/json:
{
"title": "New Blog Post",
"content": "This is the content...",
"tags": ["json", "rest-api"],
"published": false
}
Cập nhật một phần với PATCH
Sử dụng PATCH cho các cập nhật một phần. Chỉ chấp nhận các trường cần thay đổi:
// PATCH /api/users/123
{
"email": "newemail@example.com"
}
Lọc, Sắp xếp và Tìm kiếm
Sử dụng tham số truy vấn để thao tác dữ liệu, giữ cho body JSON sạch sẽ:
GET /api/users?filter[role]=admin&sort=-createdAt&search=alice
{
"status": "success",
"data": [ /* kết quả đã lọc, sắp xếp */ ]
}
Giới hạn tốc độ
Bao gồm thông tin giới hạn tốc độ trong header phản hồi và tùy chọn trong body JSON:
{
"status": "error",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later."
},
"meta": {
"rateLimit": {
"limit": 100,
"remaining": 0,
"resetAt": "2026-07-19T11:00:00Z"
}
}
}
Danh sách kiểm tra các phương pháp tốt nhất cho JSON
- [ ] Sử dụng cách đặt tên khóa nhất quán (camelCase hoặc snake_case trong toàn bộ)
- [ ] Trả về mã trạng thái HTTP phù hợp với mọi phản hồi
- [ ] Bao gồm định dạng lỗi nhất quán với mã lỗi máy có thể đọc được
- [ ] Phân trang các endpoint danh sách với một object phân trang được chuẩn hóa
- [ ] Sử dụng JSON Schema để ghi lại và xác thực cấu trúc request/response
- [ ] Đặt
Content-Type: application/jsontrên tất cả các endpoint JSON - [ ] Nén phản hồi JSON bằng gzip hoặc brotli trong môi trường production
- [ ] Thực thi giới hạn kích thước payload tối đa (ví dụ: 1MB mặc định)
- [ ] Sử dụng HTTPS để mã hóa dữ liệu JSON khi truyền tải
- [ ] Cấu trúc chi tiết lỗi để giúp máy khách gỡ lỗi mà không để lộ thông tin nội bộ
Xác thực JSON API của bạn
Chạy phản hồi API của bạn qua trình xác thực JSON để phát hiện lỗi định dạng trước khi chúng đến tay máy khách. Phản hồi JSON nhất quán, hợp lệ làm cho API của bạn đáng tin cậy và thân thiện với nhà phát triển. Sử dụng công cụ JSON Formatter & Validator của chúng tôi để kiểm tra payload của bạn trong quá trình phát triển.