Thời lượng: 20 phút
Level: Beginner
Yêu cầu: Đã hiểu về khái niệm API Contract.
Mục tiêu bài học
Sau bài học này, bạn sẽ:
- Hiểu khái niệm Response Envelope (Lớp bọc dữ liệu).
- Thiết kế được cấu trúc JSON trả về nhất quán cho mọi API.
- Biết cách xử lý metadata (phân trang, thông tin bổ sung) trong Envelope.
Nội dung chính
1. Response Envelope là gì?
Thay vì trả về dữ liệu thô (raw data), chúng ta bọc nó trong một đối tượng JSON cố định.
Tại sao cần?
- Đồng nhất: Mobile/Web app chỉ cần viết một hàm xử lý response duy nhất cho toàn bộ hệ thống.
- Thông tin bổ sung: Dễ dàng kẹp thêm các thông tin như tổng số trang, trace_id, hoặc tin nhắn thông báo mà không làm hỏng cấu trúc dữ liệu chính.
2. Cấu trúc chuẩn đề xuất
Một Response Envelope tốt thường chia làm 4 phần:
{
"success": true,
"data": { ... },
"error": null,
"meta": { ... }
}| Trường | Kiểu dữ liệu | Ý nghĩa |
|---|---|---|
success | Boolean | Thành công (true) hoặc Thất bại (false). |
data | Object / Array | Chứa dữ liệu thực tế mà người dùng yêu cầu. |
error | Object / null | Chứa thông tin lỗi nếu success là false. |
meta | Object | Chứa thông tin bổ sung (phân trang, thời gian xử lý...). |
3. Ví dụ chi tiết
Trường hợp Thành công (Lấy danh sách sản phẩm)
{
"success": true,
"data": [
{ "id": 1, "name": "iPhone 15" },
{ "id": 2, "name": "MacBook M3" }
],
"meta": {
"current_page": 1,
"total_items": 100,
"items_per_page": 20
}
}Trường hợp Thất bại (Sai mật khẩu)
{
"success": false,
"data": null,
"error": {
"code": "AUTH_001",
"message": "Mật khẩu không chính xác",
"target": "password"
},
"meta": {
"request_id": "req-99abc"
}
}Nguyên tắc then chốt
"Đừng bao giờ trả về mảng (array) ở lớp ngoài cùng của JSON."
Nếu bạn trả về [ {id:1}, {id:2} ], sau này bạn muốn thêm thông tin phân trang (pagination), bạn sẽ phải thay đổi cấu trúc hoàn toàn, gây "vỡ" client. Luôn bọc mảng trong một object: { "data": [...] }.
Thực hành
Bài tập 1: Nâng cấp API thô thành API Standard
Bối cảnh: Bạn có một API cũ trả về danh sách user như sau:
[ {"username": "huy"}, {"username": "nguyen"} ]
Yêu cầu:
- Chuyển đổi response này sang dạng Envelope.
- Thêm
request_idvào phầnmeta.
Gợi ý:
Xem đáp án mẫu
{
"success": true,
"data": [
{ "username": "huy" },
{ "username": "nguyen" }
],
"meta": {
"request_id": "888-999-000"
}
}Tình huống thực tế
Scenario 1: Hệ thống Microservice đa ngôn ngữ
Bối cảnh: Project của bạn có Service viết bằng Go, Service viết bằng Node.js. Mỗi ông viết một kiểu response khác nhau. Coder Frontend đang kêu trời vì mỗi trang phải viết code xử lý API một kiểu.
Giải pháp:
- Định nghĩa một thư viện dùng chung (Shared Library) hoặc Template chứa cấu trúc Envelope này.
- Mọi service bắt buộc phải dùng chung cấu trúc này thông qua Governance (kiểm tra ở bước Code Review).
Câu hỏi phỏng vấn
Junior/Mid Level
- Q: Tại sao không nên trả về mảng JSON trực tiếp ở root? A: Vì nó không có khả năng mở rộng. Khi cần thêm metadata (phân trang, thống kê), chúng ta buộc phải thay đổi cấu trúc response, dẫn đến việc phải cập nhật toàn bộ client (App/Web).
Tóm tắt
Những điều cần nhớ
- Envelope giúp Client code "nhàn" hơn và hệ thống chuyên nghiệp hơn.
datachỉ dành cho nội dung chính.metadành cho các thông tin vận hành và bổ trợ.errorphải chuẩn hóa mã lỗi để Client có thể xử lý logic (VD: hiển thị popup hoặc chuyển hướng).
Bước tiếp theo
Bài tiếp theo: Lesson 2.3: HTTP Semantics - Cách sử dụng đúng các mã trạng thái (200, 404, 500) và các phương thức (GET, POST, PUT) để API của bạn "chuẩn REST".