Giới thiệu về Endpoint RESTful
Thiết kế một REST API cũng giống như tạo giao diện người dùng dành cho các developer. Các endpoint là những menu điều hướng, còn payload là nội dung được hiển thị trên màn hình. Nếu việc điều hướng gây khó hiểu hoặc nội dung thiếu cấu trúc, developer sẽ gặp khó khăn khi tích hợp với hệ thống của bạn. Một REST API được thiết kế tốt sẽ tuân theo các pattern có thể dự đoán, giúp việc sử dụng trở nên trực quan và dễ dàng mà không phải liên tục tra cứu tài liệu. Bài học này tập trung vào các nguyên tắc cốt lõi để thiết kế Uniform Resource Identifier rõ ràng và cấu trúc payload JSON thực tiễn cho các resource.
Chúng ta sẽ tìm hiểu cách đặt tên endpoint đúng cách, cách định hình dữ liệu bạn gửi đi và nhận về, cũng như cách sử dụng các mã trạng thái HTTP tiêu chuẩn để truyền đạt kết quả của một thao tác. Đến cuối bài học, bạn sẽ hiểu cách xây dựng các API có trải nghiệm sử dụng tự nhiên và đủ mạnh để xử lý những mối quan hệ dữ liệu phức tạp.
Nền tảng của mọi REST API là khái niệm resource. Resource là bất kỳ thực thể nào mà ứng dụng của bạn quản lý, chẳng hạn như user, order hoặc product. Khi thiết kế endpoint, về cơ bản bạn đang xác định các địa chỉ nơi những resource này tồn tại. Ngược lại, payload là các biểu diễn thực tế của resource được truyền qua mạng, thường được định dạng bằng JavaScript Object Notation. Kết hợp địa chỉ resource hợp lý với cách biểu diễn dữ liệu rõ ràng là chìa khóa để làm chủ việc thiết kế REST API.
Cấu trúc URI của Resource
Quy tắc nền tảng nhất khi cấu trúc endpoint RESTful là sử dụng danh từ thay vì động từ. HTTP method bạn chọn, chẳng hạn như GET, POST, PUT hoặc DELETE, đã cung cấp động từ chỉ hành động. Uniform Resource Identifier của endpoint phải chỉ xác định resource được tác động. Ví dụ, endpoint để truy xuất user nên là một request GET đến path /users, thay vì một path có tên /getUsers. Điều này giúp giảm phạm vi bề mặt API và giữ cho cách đặt tên endpoint nhất quán trong toàn bộ ứng dụng.
Ngoài ra, một phương pháp tốt được chấp nhận rộng rãi là luôn sử dụng danh từ số nhiều cho các collection resource. Danh từ số nhiều cho biết endpoint đại diện cho một tập hợp các item. Khi muốn truy cập một item cụ thể trong collection đó, bạn thêm định danh duy nhất vào path. Một request GET đến /users sẽ lấy toàn bộ danh sách, trong khi một request GET đến /users/123 sẽ lấy user duy nhất có ID cụ thể đó.
Các mối quan hệ phân cấp giữa các resource cũng nên được thể hiện trong path của endpoint. Nếu một order thuộc về một user cụ thể, bạn có thể lồng các resource để thể hiện mối quan hệ này. Một request GET đến /users/123/orders cho biết rõ rằng bạn đang yêu cầu collection các order thuộc về user có ID là 123.
Tuy nhiên, bạn nên tránh lồng quá sâu. Một nguyên tắc kinh nghiệm phổ biến là giới hạn việc lồng ở một cấp. Nếu cần truy cập một resource được lồng sâu, thường tốt hơn là cung cấp một endpoint cấp cao nhất cho resource đó và sử dụng query parameter để lọc kết quả. Những path quá sâu như /users/1/orders/2/items/3 sẽ khó đọc và khó bảo trì. Thay vào đó, bạn có thể chỉ cần truy cập trực tiếp /items/3 vì ID của item là duy nhất trên toàn hệ thống.
Thiết kế mô hình request và response JSON
Khi một client tương tác với API của bạn, client gửi và nhận dữ liệu được gọi là payload. JavaScript Object Notation là tiêu chuẩn tuyệt đối cho payload của REST API nhờ tính nhẹ và khả năng được hỗ trợ rộng rãi bởi nhiều ngôn ngữ. Một payload JSON được thiết kế tốt phải dễ dự đoán, nhất quán và dễ phân tích. Luôn sử dụng một quy ước đặt tên nhất quán cho các key JSON. Snake case và camel case là hai lựa chọn phổ biến nhất. Hãy chọn một quy ước và nhất quán sử dụng quy ước đó trên mọi endpoint trong API của bạn. Việc trộn lẫn các quy ước đặt tên gây khó khăn cho các developer, vì họ phải liên tục đoán hoặc kiểm tra tài liệu để biết một field được định dạng như thế nào.
Request payload được client gửi, thường trong các thao tác POST hoặc PUT, để tạo hoặc cập nhật một resource. Các payload này chỉ nên chứa dữ liệu cần thiết cho thao tác. Bạn không bao giờ nên yêu cầu client gửi các field do server tạo, chẳng hạn như timestamp tạo hoặc mã định danh cơ sở dữ liệu nội bộ, trong request body.
Response payload được server trả về và nên cung cấp biểu diễn đầy đủ của resource. Khi trả về các collection, bạn nên bọc mảng dữ liệu trong một object thay vì trả về một mảng thô. Cách này cho phép bạn dễ dàng bổ sung metadata sau này, chẳng hạn như thông tin phân trang hoặc tổng số bản ghi, mà không phá vỡ khả năng tương thích ngược.

Hãy xem một ví dụ thực tế về việc thiết kế payload JSON cho một resource sản phẩm. Đoạn code sau minh họa cách bạn có thể cấu trúc dữ liệu JSON để tạo một sản phẩm mới.
json { "name": "Wireless Keyboard", "price": 49.99, "category": "electronics", "in_stock": true }
Khi server xử lý thành công request này, server nên trả về biểu diễn đầy đủ của resource vừa được tạo. Response này sẽ bao gồm dữ liệu do client cung cấp cùng các field do server tạo, chẳng hạn như mã định danh cơ sở dữ liệu duy nhất.
json { "data": { "id": "prod_892374982", "name": "Wireless Keyboard", "price": 49.99, "category": "electronics", "in_stock": true, "created_at": "2026-09-03T09:17:16Z" } }
Xử lý mã trạng thái HTTP và triển khai
Một khía cạnh then chốt của việc thiết kế REST API là sử dụng đúng các mã trạng thái HTTP. Những mã này cung cấp cho client ngữ cảnh tức thời về kết quả của request mà không yêu cầu client phải phân tích response body. Nhóm mã 200 cho biết thao tác thành công. Sử dụng 200 OK cho các request thành công thông thường, chẳng hạn như khi truy xuất dữ liệu. Sử dụng 201 Created khi một request POST tạo thành công một resource mới. Sử dụng 204 No Content khi thao tác thành công nhưng không có dữ liệu để trả về, chẳng hạn như sau một thao tác DELETE thành công.
Nhóm mã 400 cho biết lỗi từ phía client. Sử dụng 400 Bad Request khi client gửi JSON không hợp lệ hoặc không vượt qua các quy tắc validation. Sử dụng 401 Unauthorized khi xác thực thất bại và 403 Forbidden khi người dùng không có quyền. Cuối cùng, sử dụng 404 Not Found khi Uniform Resource Identifier (URI) của resource được yêu cầu không tồn tại.
Để cụ thể hóa các khái niệm này, chúng ta có thể triển khai một endpoint đơn giản bằng một framework web hiện đại. Ví dụ sau sử dụng JavaScript và Express để minh họa cách kết hợp endpoint dạng số nhiều, trích xuất payload JSON và sử dụng mã trạng thái phù hợp.
javascript const express = require('express'); const app = express(); app.use(express.json());
const users = [];
app.post('/users', (req, res) => { const payload = req.body;
if (!payload.email || !payload.name) { return res.status(400).json({ error: "Missing required fields email and name" }); }
const newUser = { id: users.length + 1, name: payload.name, email: payload.email, createdAt: new Date().toISOString() };
users.push(newUser);
return res.status(201).json({ data: newUser }); });
app.listen(3000, () => console.log('API running on port 3000'));
Trong phần triển khai này, chúng ta định nghĩa một route POST trên endpoint dạng số nhiều /users. Chúng ta trích xuất payload JSON từ phần thân request và thực hiện bước kiểm tra cơ bản. Nếu kiểm tra thất bại, chúng ta lập tức trả về mã trạng thái 400 Bad Request cùng với thông báo lỗi mô tả rõ vấn đề. Nếu payload hợp lệ, chúng ta mô phỏng việc lưu resource vào cơ sở dữ liệu và trả về toàn bộ biểu diễn của resource được bọc trong thuộc tính data, cùng với mã trạng thái 201 Created.
Các lỗi thường gặp và phần tóm tắt
Một trong những lỗi phổ biến nhất mà người mới bắt đầu mắc phải là đưa các hành động vào Uniform Resource Identifier. Các endpoint như POST /createUser hoặc GET /deleteOrder vi phạm các nguyên tắc REST vì bỏ qua ý nghĩa ngữ nghĩa của các phương thức HTTP. Hãy luôn dựa vào method để xác định hành động và path để xác định resource. Một lỗi phổ biến khác là bỏ qua các mã trạng thái HTTP và luôn trả về trạng thái 200 OK ngay cả khi xảy ra lỗi, đồng thời nhúng thông tin chi tiết về lỗi thực tế vào phần thân JSON. Điều này buộc client phải phân tích mọi response chỉ để xác định request có thành công hay không, làm mất đi mục đích của các giao thức mạng tiêu chuẩn.

Tóm lại, thiết kế REST API hiệu quả đòi hỏi tính kỷ luật và nhất quán. Bạn phải sử dụng danh từ số nhiều cho các endpoint để biểu thị các tập hợp tài nguyên. Bạn phải tận dụng các đường dẫn phân cấp để biểu thị mối quan hệ, đồng thời tránh lồng ghép quá mức. Payload JSON của bạn nên sử dụng quy ước đặt tên nhất quán và phân tách dữ liệu do client cung cấp khỏi siêu dữ liệu do server tạo. Cuối cùng, bạn phải sử dụng đầy đủ các mã trạng thái HTTP tiêu chuẩn để truyền đạt rõ ràng trạng thái thành công và thất bại. Tuân thủ những thực hành này sẽ giúp bạn xây dựng các giao diện lập trình ứng dụng mạnh mẽ, có khả năng mở rộng và thân thiện với nhà phát triển.
Bài kiểm tra cuối bài
Ôn lại kiến thức, thử sức và nhận góp ý cho bài làm.