10 quy tắc đặt tên REST API nhất quán
Mở bất kỳ codebase nào cũ hơn hai năm, bạn sẽ tìm thấy những vết sẹo: /getUser, /user_list, `[REDACTED PATH]
{% cta https://apidog.com/?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation %} Dùng thử Apidog hôm nay {% endcta %}
Đặt tên là quyết định thiết kế API rẻ nhất để đưa ra nhưng tốn kém nhất để thay đổi. Khi client đã phụ thuộc vào /getOrders, bạn có thể phải duy trì nó trong nhiều năm.
Bài viết này đưa ra quy tắc cụ thể cho từng quyết định đặt tên trong REST API, kèm ví dụ nên và không nên. Nội dung bổ sung cho hướng dẫn REST API dành cho nhà phát triển, nhưng tập trung vào vấn đề gây tranh luận nhiều nhất: đặt tên.
1. Dùng danh từ số nhiều cho collection
URL nên mô tả tài nguyên, không phải thao tác. Collection chứa nhiều tài nguyên nên dùng danh từ số nhiều.
Nên:
http
GET /v1/products
GET /v1/products/89
GET /v1/orders
Không nên:
http
GET /v1/getProducts
GET /v1/product
GET /v1/productList
Cách này nhất quán ở cả hai cấp:
-
/products: collection sản phẩm -
/products/89: sản phẩm có ID89trong collection
Danh từ số ít dễ tạo ra các URL khó hiểu như /product cho nhiều sản phẩm và /product/89 cho một sản phẩm. Hướng dẫn REST API của Microsoft và nhiều API công khai như Stripe, GitHub, Shopify đều dùng danh từ số nhiều.
Ngoại lệ: singleton thực sự. Nếu mỗi người dùng chỉ có một giỏ hàng, `[REDACTED PATH] hợp lý. Không cần dùng số nhiều cho tài nguyên luôn có đúng một thể hiện.
2. Không dùng động từ trong path
Phương thức HTTP đã là động từ. Đừng lặp lại thông tin đó trong URL.
Nên:
GET /v1/orders/42
DELETE /v1/orders/42
PATCH /v1/orders/42
Không nên:
GET /v1/fetchOrder/42
POST /v1/deleteOrder/42
POST /v1/updateOrderStatus
Path dựa trên động từ cũng làm tăng bề mặt API. Một tài nguyên với bốn phương thức trở thành bốn endpoint cần tài liệu hóa, kiểm thử và cache riêng.
Ví dụ, CDN có thể liên kết GET /v1/orders/42 với việc vô hiệu hóa cache sau DELETE /v1/orders/42 vì chúng dùng cùng một URL. CDN không thể tự hiểu rằng /fetchOrder/42 và /deleteOrder/42 cùng tác động lên một tài nguyên.
3. Dùng kebab-case cho URL path
Các segment có nhiều từ nên dùng dấu gạch nối.
Nên:
/v1/gift-cards
/v1/shipping-addresses
Không nên:
/v1/giftCards
/v1/gift_cards
/v1/GiftCards
Có ba lý do:
- Google coi dấu gạch nối là dấu phân cách từ, giúp tài liệu API dễ lập chỉ mục hơn.
- Dấu gạch dưới dễ bị che khuất khi URL được gạch chân trong email hoặc tài liệu.
- CamelCase dễ dẫn đến lỗi viết hoa:
/giftCardsvà/giftcardsthường là hai URL khác nhau.
Hướng dẫn RESTful API của Zalando xem kebab-case là quy tắc bắt buộc và áp dụng nó cho hàng trăm dịch vụ nội bộ.
4. Chọn một kiểu chữ cho JSON và ghi thành tài liệu
Với tên trường trong request và response, cả camelCase lẫn snake_case đều phù hợp. Vấn đề là trộn lẫn chúng.
Nên chọn một kiểu và dùng nhất quán:
{
"orderId": 42,
"createdAt": "2026-08-30T09:15:00Z",
"totalAmount": 4999
}
Hoặc:
{
"order_id": 42,
"created_at": "2026-08-30T09:15:00Z",
"total_amount": 4999
}
Không nên:
{
"orderId": 42,
"created_at": "2026-08-30T09:15:00Z",
"TotalAmount": 4999
}
camelCase thuận tiện cho client JavaScript và Java. snake_case dễ đọc, phù hợp với Ruby, Python và tên cột SQL; Stripe dùng kiểu này trên toàn bộ API.
Hãy chọn theo nhóm người dùng API chính, sau đó ghi vào style guide và kiểm tra trong quy trình review schema. Trộn lẫn kiểu chữ giữa các endpoint thường là vấn đề quản trị, không phải vấn đề sở thích.
5. Giới hạn lồng ghép ở tối đa hai cấp
Lồng ghép hữu ích để thể hiện quyền sở hữu:
[REDACTED PATH]
có nghĩa là “các đơn hàng thuộc về người dùng 42”.
Nên:
GET /v1[REDACTED PATH]
GET /v1/orders/1337/refunds
Không nên:
GET /v1[REDACTED PATH]/7/status
Lồng ghép quá sâu buộc client phải truyền tất cả ID của tài nguyên cha để truy cập tài nguyên con, ngay cả khi tài nguyên con đã có ID duy nhất.
Nếu khoản hoàn tiền có ID 7, hãy cung cấp:
/refunds/7
hoặc:
/orders/1337/refunds/7
Một quy tắc nhanh: nếu URL có từ ba ID trở lên, hãy cân nhắc làm phẳng nó. Khi đơn hàng đã tồn tại, nó không còn cần user_id trong path; /orders/1337 có thể đứng độc lập.
6. Đưa lọc, sắp xếp và phân trang vào query parameters
Path xác định tài nguyên. Query parameters thay đổi cách truy vấn hoặc hiển thị tài nguyên.
Nên:
GET /v1/orders?status=active&sort=-created_at&limit=50&cursor=eyJpZCI6NDJ9
GET /v1/products?category=electronics&min_price=1000
Không nên:
GET /v1/orders/active
GET /v1/orders/sorted-by-date-desc
GET /v1/getOrdersByStatusAndDate
Mẫu sort=-created_at dùng dấu trừ để biểu thị thứ tự giảm dần, giúp tránh phải thêm tham số order=desc. Các path như /orders/active có vẻ đơn giản nhưng nhanh chóng dẫn đến một endpoint mới cho từng tổ hợp bộ lọc.
Tên tham số phân trang cũng phải nhất quán. Chọn một trong các hướng:
limit/cursor
hoặc:
page/per_page
và dùng lại trên mọi collection. Xem thêm hướng dẫn phân trang API để hiểu rõ đánh đổi giữa cursor và offset.
7. Đặt phiên bản chính trong path
Hai cách phổ biến là:
/v1/products
hoặc phiên bản trong header:
Accept: application/vnd.myapi.v1+json
Phiên bản trong header “thuần REST” hơn vì URL giữ nguyên tên tài nguyên. Hướng dẫn thiết kế API của Google ghi nhận cả hai phương pháp.
Tuy nhiên, phiên bản trong path thường dễ vận hành hơn:
- xuất hiện trong mọi log;
- có thể kiểm tra trực tiếp từ trình duyệt;
- dễ cache mà không cần xử lý
Vary; - client không thể vô tình quên phiên bản.
Vì vậy, ưu tiên:
/v1/products
Chỉ dùng phiên bản chính như /v1, không dùng /v1.2. Thay đổi nhỏ nên được triển khai theo hướng bổ sung và không phá vỡ tương thích. Xem so sánh các chiến lược quản lý phiên bản API để xem cây quyết định đầy đủ, bao gồm content negotiation.
8. Xem ID là giá trị opaque
Các ID tuần tự như sau:
/orders/41
/orders/42
/orders/43
có thể tiết lộ số lượng đơn hàng và hỗ trợ enumeration attack. Kẻ tấn công chỉ cần duyệt không gian ID để tìm lỗi phân quyền. Broken Object Level Authorization (BOLA) đứng đầu OWASP API Security Top 10.
Nên:
GET /v1/orders/ord_9f8e2a71b3
GET /v1[REDACTED PATH]e8400-e29b-41d4-a716-446655440000
Không nên khi việc liệt kê là vấn đề:
GET /v1/orders/42
GET /v1/invoices/10883
ID ngẫu nhiên có tiền tố, tương tự ord_9f8e2a71b3 của Stripe, có ba ưu điểm:
- khó đoán;
- dễ nhận diện trong log;
- an toàn hơn khi bị công khai.
Kiểm tra phân quyền vẫn là bắt buộc. Opaque ID chỉ giảm tác động khi kiểm tra bị thiếu; nó không thay thế authorization. Bạn vẫn có thể dùng khóa chính dạng số nguyên bên trong hệ thống, miễn là không tiết lộ chúng trong URL.
9. Mô hình hóa hành động không phải CRUD thành controller resource
Các hành động như hủy đơn hàng, thử lại thanh toán hoặc gửi lại email không phải lúc nào cũng ánh xạ rõ ràng vào CRUD.
Nên:
POST /v1/orders/42/cancel
POST /v1/payments/pay_88a1/retry
Không nên:
PATCH /v1/orders/42
{ "status": "cancelled" }
POST /v1/cancelOrder
{ "orderId": 42 }
Đây là ngoại lệ được phép đối với quy tắc “không dùng động từ”: động từ nằm ở cuối path và bị giới hạn trong tài nguyên chịu tác động.
PATCH {"status":"cancelled"} che giấu một state machine bên trong việc cập nhật trường. Hủy đơn hàng có thể đồng thời:
- hoàn tiền;
- giải phóng tồn kho;
- gửi thông báo;
- ghi audit log.
Endpoint /cancel thể hiện rõ ý định, cho phép cấp quyền và ghi log riêng, đồng thời hỗ trợ input cụ thể như lý do hủy.
10. Nhất quán về kiểu chữ của header và query parameters
Header tùy chỉnh nên dùng Hyphenated-Pascal-Case, phù hợp với quy ước HTTP:
Idempotency-Key: ...
[REDACTED IDENTIFIER]
Không thêm tiền tố X- cũ; tiền tố này đã bị phản đối bởi RFC 6648 từ năm 2012. Tên header không phân biệt hoa thường khi truyền tải, nhưng tài liệu và SDK vẫn nên trình bày nhất quán.
Query parameters nên khớp với kiểu chữ của JSON. Nếu JSON dùng snake_case, hãy dùng:
?min_price=1000&created_after=2026-01-01
thay vì:
?minPrice=1000
Việc phải đọc created_at trong response nhưng gõ createdAfter trong query là nguồn lỗi không cần thiết.
Bảng tóm tắt
| # | Quy tắc | Nên | Không nên |
|---|---|---|---|
| 1 | Danh từ số nhiều cho collection |
/products, /products/89
|
/getProducts, /productList
|
| 2 | Không dùng động từ trong path | DELETE /orders/42 |
POST /deleteOrder/42 |
| 3 | Path dùng kebab-case
|
/gift-cards |
/giftCards, /gift_cards
|
| 4 | Một kiểu chữ JSON có tài liệu |
order_id ở mọi nơi |
Trộn orderId và order_id
|
| 5 | Tối đa hai cấp lồng ghép | /orders/1337/refunds |
`[REDACTED PATH] |
| 6 | Lọc và phân trang trong query | ?status=active&sort=-created_at |
/orders/active |
| 7 | Phiên bản chính trong path | /v1/products |
/v1.2/products, header phiên bản |
| 8 | ID tài nguyên opaque | /orders/ord_9f8e2a71b3 |
/orders/42 công khai, dễ liệt kê |
| 9 | Controller resource cho hành động | POST /orders/42/cancel |
PATCH {"status":"cancelled"} |
| 10 | Kiểu chữ nhất quán cho header/query |
Idempotency-Key, ?min_price=
|
X-IDEMPOTENCY_KEY, ?minPrice=
|
Áp dụng quy ước ở quy mô lớn
Một style guide trên wiki sẽ không đủ. Các đội duy trì API nhất quán thường thiết kế trước và áp dụng quy ước trước khi viết code — đây là nền tảng của quản trị API.
Apidog hỗ trợ quy trình schema-first bằng công cụ thiết kế trực quan:
- path, kiểu chữ và tên tham số trở thành thành phần thiết kế rõ ràng;
- schema dùng chung như
Pagination,ErrorvàMoneychỉ cần định nghĩa một lần; - review trong workspace giúp phát hiện
/getUserOrderstrước khi client tích hợp; - đặc tả có thể thúc đẩy tạo tài liệu, mock server và kiểm thử.
Khi tên endpoint đã được duyệt trong schema, đó cũng là tên đội ngũ sẽ triển khai. Bạn có thể tải Apidog và dùng thử miễn phí. Nâng cấp API cũ rất khó; giữ API mới nhất quán thì đơn giản hơn nhiều.
Câu hỏi thường gặp
URL REST nên dùng số nhiều hay số ít?
Dùng số nhiều cho mọi tài nguyên có nhiều hơn một thể hiện:
text
/products
/orders
/users
Dạng số nhiều vẫn tự nhiên cho cả collection (/orders) và một phần tử (/orders/42). Chỉ dùng số ít cho singleton thực sự, chẳng hạn `[REDACTED PATH]apidog.com/vi/blog/what-is-rest-api?utm_source=dev.to&utm_medium=wanda&utm_content=n8n-post-automation).
camelCase hay snake_case tốt hơn cho JSON?
Không có lựa chọn tuyệt đối tốt hơn:
-
camelCasephù hợp với người dùng JavaScript; -
snake_casedễ đọc và khớp với Python, Ruby, SQL cũng như API của Stripe.
Hãy chọn một kiểu, ghi vào style guide và kiểm tra trong schema review. Trộn lẫn giữa các endpoint gây hại nhiều hơn bản thân lựa chọn kiểu chữ.
Nên đặt phiên bản API trong URL hay header?
Ưu tiên path như /v1/orders, trừ khi bạn có yêu cầu mạnh về hypermedia. Phiên bản trong path xuất hiện rõ trong log, cache và browser test; client cũng không thể quên thêm header. Chỉ dùng phiên bản chính và triển khai thay đổi nhỏ theo hướng tương thích ngược.
Có bao giờ được dùng động từ trong REST API path không?
Có, đối với controller endpoint cho hành động không phải CRUD:
POST /orders/42/cancel
POST /payments/pay_88a1/retry
Động từ phải nằm ở cuối path, giới hạn trong tài nguyên chịu tác động và dùng phương thức POST. Ngoài trường hợp này, phương thức HTTP mang động từ còn path chỉ chứa danh từ.
Top comments (0)