💡 Key Takeaways
- Resource Naming: The Foundation That Everyone Gets Wrong
- HTTP Methods and Status Codes: Speak the Language Correctly
- Versioning: Future-Proofing Without the Pain
- Pagination, Filtering, and Sorting: Handling Large Datasets
Ba năm trước, tôi đã chứng kiến một startup tiêu tốn 2,3 triệu đô la trong vòng tài trợ vì thiết kế API của họ bị lỗi cơ bản đến mức mọi tính năng mới đều cần phải viết lại một nửa mã nguồn. Tôi là Sarah Chen, và tôi đã dành 12 năm qua với vai trò kiến trúc sư API chính tại ba startup kỳ lân khác nhau, thiết kế các hệ thống xử lý hơn 47 tỷ yêu cầu mỗi tháng. Những gì tôi đã học được là thiết kế API REST không phải là tuân theo các quy tắc cứng nhắc—mà là đưa ra những lựa chọn có chủ đích dẫn đến sự xuất sắc về kỹ thuật hoặc nợ kỹ thuật.
💡 Những Điều Cần Lưu Ý Quan Trọng
- Đặt Tên Tài Nguyên: Nền Tảng Mà Mọi Người Đều Sai
- Phương Thức HTTP và Mã Trạng Thái: Nói Đúng Ngôn Ngữ
- Phiên Bản: Bảo Vệ Tương Lai Mà Không Gặp Khó Khăn
- Phân Trang, Lọc và Sắp Xếp: Xử Lý Tập Dữ Liệu Lớn
Cảnh quan đã thay đổi đáng kể kể từ khi REST trở thành tiêu chuẩn mặc định. Vào năm 2026, chúng ta đang đối mặt với điện toán biên, các ứng dụng do AI cung cấp có hàng nghìn cuộc gọi API mỗi giây, và người dùng mong đợi thời gian phản hồi dưới 100ms từ bất kỳ đâu trên hành tinh. Lời khuyên "chỉ cần tuân theo các nguyên tắc REST" không còn hiệu quả nữa. Bạn cần một danh sách kiểm tra thiết thực, đã được thử nghiệm thực tiễn, phù hợp với các thực tế hiện đại.
Bài viết này chính là danh sách kiểm tra đó. Tôi sẽ chia sẻ chính xác khung mà tôi sử dụng khi thiết kế API cho các công ty có doanh thu hàng triệu đô la mỗi ngày. Đây không phải là những thực tiễn lý thuyết—chúng là các mẫu hình tách biệt các API có thể mở rộng với các API bị sụp đổ dưới trọng lượng của chính chúng.
Đặt Tên Tài Nguyên: Nền Tảng Mà Mọi Người Đều Sai
Cho phép tôi bắt đầu với một điều có vẻ tầm thường nhưng gây ra nhiều vấn đề hơn hầu hết mọi thứ khác: đặt tên tài nguyên. Tôi đã xem xét hơn 200 thiết kế API trong sự nghiệp của mình, và tôi ước tính rằng 60% trong số đó có cách đặt tên tài nguyên không nhất quán hoặc gây nhầm lẫn, tạo ra các vấn đề lan truyền.
Đây là nguyên tắc chính: các URL của bạn nên đọc như các câu mô tả thứ bậc tài nguyên. Sử dụng danh từ số nhiều cho các bộ sưu tập, giữ cho độ lồng nhau nông (tối đa 2-3 cấp độ) và luôn nhất quán. Khi tôi gia nhập công ty hiện tại, API của họ có các điểm cuối như /getUser, /user-list, và /users/fetch—tất cả đều thực hiện những điều tương tự. Chúng tôi đã mất ba tháng để giải quyết hỗn loạn ấy.
Mẫu mà tôi khuyến nghị:
- Bộ sưu tập:
/api/v1/users,/api/v1/orders,/api/v1/products - Tài nguyên cụ thể:
/api/v1/users/12345,/api/v1/orders/ord_abc123 - Phụ tài nguyên:
/api/v1/users/12345/orders,/api/v1/orders/ord_abc123/items - Hành động trên tài nguyên:
/api/v1/orders/ord_abc123/cancel(POST),/api/v1/users/12345/verify-email(POST)
Chú ý những gì thiếu? Động từ trong đường dẫn URL (trừ các hành động không phải CRUD). Phương thức HTTP chính là động từ. GET /users có nghĩa là "lấy người dùng." POST /users có nghĩa là "tạo một người dùng." DELETE /users/123 có nghĩa là "xóa người dùng 123." Điều này không chỉ là thẩm mỹ—nó làm cho API của bạn dự đoán được và giảm tải nhận thức cho các nhà phát triển.
Đối với các hoạt động không phải CRUD, tôi sử dụng cách tiếp cận thực dụng. Đúng, các người theo chủ nghĩa thuần túy sẽ nói mọi thứ nên phù hợp với CRUD, nhưng trong thế giới thực, bạn có các hoạt động như "hủy một đơn hàng," "xác thực một email," hoặc "tính toán phí vận chuyển." Tôi đại diện cho những điều này dưới dạng yêu cầu POST đến các điểm cuối hành động: POST /orders/123/cancel. Chìa khóa là tính nhất quán—hãy ghi lại mẫu của bạn và tuân thủ nó một cách nghiêm ngặt.
Một chi tiết quan trọng nữa: sử dụng kiểu chữ kebab cho các tài nguyên nhiều từ (/shipping-addresses, không phải /shippingAddresses hoặc /shipping_addresses). Các URL không phân biệt chữ hoa chữ thường trong nhiều ngữ cảnh, và kiểu chữ kebab là định dạng dễ đọc nhất. Tôi đã thấy các sự cố sản xuất gây ra bởi các vấn đề nhạy cảm với chữ cái có thể đã tránh được nhờ quy tắc đơn giản này.
Phương Thức HTTP và Mã Trạng Thái: Nói Đúng Ngôn Ngữ
Nếu đặt tên tài nguyên là từ vựng của API của bạn, thì các phương thức HTTP và mã trạng thái là ngữ pháp của nó. Và giống như trong ngôn ngữ con người, việc sử dụng ngữ pháp sai làm bạn khó hiểu—ngay cả khi mọi người cuối cùng có thể hiểu ý của bạn.
Tôi thấy hai mẫu chống lại phổ biến lặp đi lặp lại. Đầu tiên, các API sử dụng POST cho mọi thứ vì "nó hoạt động." Thứ hai, các API trả về 200 OK cho mọi phản hồi, ngay cả lỗi, với trạng thái thực tế bị chôn vùi trong cơ thể phản hồi. Cả hai mẫu này tạo ra các API khó cache, khó gỡ lỗi và khó tích hợp với các công cụ tiêu chuẩn.
Đây là phân tích của tôi theo từng phương thức dựa trên việc sử dụng thực tế:
GET: Lấy tài nguyên. Phải idempotent và an toàn (không có tác dụng phụ). Không bao giờ sử dụng GET cho các hoạt động thay đổi trạng thái—tôi không quan tâm nếu đó chỉ là "cập nhật một dấu thời gian đã truy cập gần đây." Đó là công việc của phần mềm trung gian. Các yêu cầu GET nên có thể lưu trữ, và việc trộn lẫn thay đổi trạng thái làm phá vỡ giả định về cache. Trong một hệ thống mà tôi đã làm việc, chúng tôi đã thấy giảm 73% tải cơ sở dữ liệu chỉ bằng cách thực hiện đúng tính idempotency của GET và thêm các tiêu đề cache HTTP.
POST: Tạo tài nguyên mới hoặc kích hoạt hành động. POST là trụ cột cho các hoạt động không idempotent. Khi tạo tài nguyên, trả về 201 Created với tiêu đề Location chỉ đến tài nguyên mới. Khi kích hoạt hành động, trả về 200 OK hoặc 202 Accepted (đối với các hoạt động không đồng bộ) với một cơ thể phản hồi mô tả kết quả.
PUT: Thay thế toàn bộ tài nguyên. Đây là nơi mà nhiều nhà phát triển bị nhầm lẫn. PUT nên thay thế tài nguyên hoàn toàn với đại diện đã cung cấp. Nếu bạn gửi yêu cầu PUT chỉ với một số trường, thì đó chỉ là những trường mà tài nguyên nên có sau đó (các trường khác nên được đặt thành mặc định hoặc null). Trên thực tế, tôi sử dụng PUT một cách tiết kiệm—thường chỉ cho các tài nguyên mà khách hàng thực sự quản lý trạng thái hoàn chỉnh.
PATCH: Cập nhật một phần tài nguyên. Đây là những gì mà hầu hết các nhà phát triển thực sự muốn khi họ nghĩ họ muốn PUT. PATCH cho phép bạn gửi chỉ những trường bạn muốn thay đổi. Tôi thường sử dụng JSON Patch (RFC 6902) hoặc JSON Merge Patch (RFC 7396) cho định dạng yêu cầu. Tại công ty hiện tại của tôi, 94% các thao tác cập nhật của chúng tôi sử dụng PATCH, không phải PUT.
DELETE: Xóa một tài nguyên. Trả về 204 No Content khi thành công (không cần phản hồi), hoặc 200 OK nếu bạn đang trả về thông tin về sự xóa. Đảm bảo DELETE là idempotent—gọi nó nhiều lần nên có cùng tác dụng như gọi một lần. Trả về 204 ngay cả khi tài nguyên đã được xóa.
Đối với mã trạng thái, tôi sử dụng tập con thực tiễn này bao phủ 99% các tình huống:
- 200 OK: GET, PUT, PATCH, hoặc POST thành công trả về dữ liệu
- 201 Created: POST thành công tạo ra một tài nguyên
- 202 Accepted: Yêu cầu đã được chấp nhận để xử lý không đồng bộ
- 204 No Content: DELETE hoặc cập nhật thành công không có phản hồi
- 400 Bad Request: Khách hàng đã gửi dữ liệu không hợp lệ (với thông điệp lỗi chi tiết)
- 401 Unauthorized: Cần xác thực hoặc thất bại
- 403 Forbidden: Đã xác thực nhưng không được ủy quyền cho tài nguyên này
- 404 Not Found: Tài nguyên không tồn tại
- 409 Conflict: Yêu cầu xung đột với trạng thái hiện tại (ví dụ, email trùng lặp)
- 422 Unprocessable Entity: Kiểm tra đã thất bại (tôi ưu tiên điều này hơn 400 cho các lỗi kiểm tra)
- 429 Too Many Requests: Vượt quá giới hạn tỷ lệ
- 500 Internal Server Error: Điều gì đó đã hỏng ở phía chúng tôi
- 503 Service Unavailable: Gián đoạn tạm thời hoặc bảo trì
Thông điệp quan trọng: mã trạng thái dành cho ngữ nghĩa cấp HTTP, cơ thể phản hồi dành cho các chi tiết cấp ứng dụng. Một phản hồi 400 luôn có nghĩa là "bạn đã gửi dữ liệu xấu," nhưng cơ thể phản hồi giải thích chính xác những gì sai với trường nào.
Phiên Bản: Bảo Vệ Tương Lai Mà Không Gặp Khó Khăn
Tôi đã trải qua ba lần di chuyển phiên bản API lớn, và mỗi lần đều dạy tôi điều gì đó đau đớn về những điều không nên làm. Tồi tệ nhất là một lần di chuyển từ v1 lên v2 kéo dài 18 tháng và yêu cầu phối hợp cập nhật trên 47 ứng dụng khách. Chúng tôi đã mất hai khách hàng lớn trong quá trình đó vì các đội tích hợp của họ không thể theo kịp các thay đổi.
| Cách Tiếp Cận | Mẫu URL | Khả Năng Mở Rộng | Khả Năng Bảo Trì |
|---|---|---|---|
| RESTful (Đề Xuất) | /users/{id}/orders | Tuyệt vời - Cấu trúc rõ ràng, có thể lưu trữ | Cao - Mẫu dự đoán được |
| RPC-Style (Mẫu Chống lại) | /getUser, /fetchOrders | Kém - Không có cache, dựa vào động từ | Thấp - Đặt tên không nhất quán |