Trước hết phải chốt ngữ nghĩa với bên gọi, vì đây là câu hỏi nghiệp vụ chứ không phải câu hỏi HTTP:
Cách 1 — all-or-nothing (atomic). Cả batch chạy trong một transaction, một bản ghi lỗi thì rollback toàn bộ, trả 422 kèm danh sách bản ghi sai. Dễ hiểu, dữ liệu luôn nhất quán. Nhược điểm: batch lớn dễ bị một bản ghi rác chặn cả lô, và transaction dài gây khoá.
Cách 2 — partial success. Xử lý từng item độc lập, trả 200 (hoặc 207 Multi-Status) với kết quả từng item, thứ tự khớp với input:
HTTP/1.1 200 OK
{
"results": [
{ "index": 0, "id": "a1", "status": 200 },
{ "index": 1, "status": 422,
"error": { "type": ".../validation", "detail": "price must be > 0" } },
{ "index": 2, "id": "a3", "status": 200 }
],
"succeeded": 497, "failed": 3
}Điều quan trọng: đừng trả 200 rồi để client tự đoán, và cũng đừng trả 500 khi 497/500 bản ghi đã ghi thành công — 5xx sẽ khiến client retry cả lô và ghi đè lần nữa.
Những thứ luôn cần kèm theo:
- Giới hạn kích thước batch (vd tối đa 1000 item) và trả 413/400 khi vượt.
- Idempotency key cho cả batch, hoặc client_reference_id cho từng item, để client retry an toàn phần lỗi.
- Batch quá lớn thì đổi sang mô hình async job (202 + job id) thay vì cố xử lý trong một request.