REST là gì?

REST (Representational State Transfer) là một phong cách kiến trúc, không phải giao thức. Nó định nghĩa một tập các ràng buộc về cách các web service nên hoạt động. Khi một API tuân theo các ràng buộc này, ta gọi nó là RESTful. Ý tưởng cốt lõi rất đơn giản: client gửi một request đến một URL đại diện cho một resource, và server phản hồi bằng một representation của resource đó — thường là JSON.

Sáu ràng buộc định nghĩa REST gồm: phân tách client-server, stateless, khả năng cache, giao diện thống nhất, hệ thống phân lớp, và code on demand (tùy chọn). Trong thực tế, các ràng buộc quan trọng nhất hằng ngày là stateless và giao diện thống nhất. Stateless nghĩa là mỗi request phải chứa toàn bộ thông tin cần thiết để xử lý — server không lưu trạng thái phiên giữa các request. Giao diện thống nhất nghĩa là sử dụng các phương thức HTTP chuẩn và các mẫu URL dễ dự đoán.

sơ đồ minh họa chu trình request-response stateless giữa client-server trong REST với representation của resource

Các phương thức HTTP và ý nghĩa

Phương thức HTTP cho server biết cần thực hiện hành động gì trên một resource. Mỗi phương thức mang một ngữ nghĩa cụ thể mà bạn cần tôn trọng — bỏ qua điều này sẽ dẫn đến các API khó hiểu và khó sử dụng.

GET dùng để lấy một resource mà không gây ra tác dụng phụ. POST dùng để tạo một resource mới. PUT dùng để thay thế toàn bộ một resource đã tồn tại. PATCH dùng để cập nhật một phần resource. DELETE dùng để xóa một resource.

Dưới đây là cách các phương thức này ánh xạ vào một resource users điển hình:

text
GET /users → list all users
GET /users/42 → get user with id 42
POST /users → create a new user
PUT /users/42 → replace user 42 entirely
PATCH /users/42 → update specific fields of user 42
DELETE /users/42 → delete user 42

GET và DELETE là idempotent — gọi nhiều lần cho cùng kết quả. PUT cũng là idempotent. POST thì không — gọi POST hai lần thường tạo ra hai resource.

Mã trạng thái HTTP

Mã trạng thái cho client biết chuyện gì đã xảy ra. Chúng được chia thành năm nhóm: 1xx thông tin, 2xx thành công, 3xx chuyển hướng, 4xx lỗi client, 5xx lỗi server. Những mã bạn sẽ sử dụng thường xuyên nhất trong phát triển API là:

200 OK — yêu cầu thành công và có response body. Dùng mã này cho các phản hồi GET và PATCH thành công.

201 Created — một tài nguyên đã được tạo thành công. Dùng mã này sau khi POST thành công. Kèm theo header Location trỏ đến tài nguyên mới.

204 No Content — thành công, nhưng không có body để trả về. Dùng mã này cho DELETE.

400 Bad Request — client gửi dữ liệu không hợp lệ (thiếu trường, sai kiểu dữ liệu, lỗi validation).

401 Unauthorized — yêu cầu thiếu thông tin xác thực hợp lệ.

403 Forbidden — client đã xác thực nhưng không có quyền truy cập.

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ụ: tạo người dùng với email đã tồn tại).

422 Unprocessable Entity — yêu cầu có cú pháp đúng nhưng không thỏa mãn validation ngữ nghĩa.

500 Internal Server Error — có lỗi xảy ra phía server. Không bao giờ để lộ stack trace ở đây trong môi trường production.

Một ví dụ cụ thể trong một handler của Express:

javascript
app.post('/users', (req, res) => {
const { email, name } = req.body;
if (!email || !name) {
return res.status(400).json({ error: 'email and name are required' });
}
// imagine createUser() saves to a database and returns the new record
const user = createUser({ email, name });
res
.status(201)
.location(`/users/${user.id}`)
.json(user);
});
sơ đồ luồng cây quyết định mã trạng thái HTTP: nhánh thành công 2xx so với lỗi phía client 4xx và lỗi phía server 5xx

HTTP Headers trong ngữ cảnh API

Headers chứa metadata về request hoặc response. Có một vài header thiết yếu cho REST API.

Content-Type khai báo định dạng của body request hoặc response. Đối với JSON API, giá trị này luôn là application/json. Express sẽ tự động thiết lập khi bạn gọi res.json(), nhưng khi xây dựng response thô, bạn phải thiết lập một cách tường minh.

Accept cho server biết định dạng mà client có thể xử lý. Client nên gửi Accept: application/json khi gọi một JSON API.

Authorization mang theo thông tin xác thực. Mẫu phổ biến nhất cho REST API là xác thực bằng Bearer token:

text
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Cache-Control chỉ định hành vi caching cho các response của GET. Với dữ liệu công khai ít khi thay đổi, việc đặt Cache-Control: public, max-age=300 giúp giảm tải cho server. Với dữ liệu riêng của người dùng hoặc dữ liệu nhạy cảm, hãy dùng Cache-Control: no-store.

Trong Express, bạn có thể đọc các header đến qua req.headers và thiết lập header đi qua res.set():

javascript
app.get('/products', (req, res) => {
const products = getProducts();
res
.set('Cache-Control', 'public, max-age=300')
.json(products);
});

Tóm tắt

REST là một kiểu kiến trúc được xây dựng trên nền tảng giao tiếp client-server stateless với một giao diện thống nhất. Các phương thức HTTP định nghĩa hành động cần thực hiện trên một tài nguyên — GET để đọc, POST để tạo, PUT để thay thế, PATCH để cập nhật, DELETE để xóa. Mã trạng thái truyền tải chính xác kết quả: 2xx cho thành công, 4xx cho lỗi phía client, 5xx cho lỗi phía server. Headers chứa metadata như kiểu nội dung, thông tin xác thực và chỉ thị cache. Nắm vững các nguyên lý nền tảng này chính là điều phân biệt một API dễ tích hợp với một API buộc người dùng phải đoán.

Bài kiểm tra cuối bài

1. Ràng buộc REST nào có nghĩa là mỗi request phải chứa toàn bộ thông tin cần thiết để xử lý, và server không lưu trữ trạng thái phiên giữa các request?

2. Bạn muốn cập nhật một phần địa chỉ email của người dùng mà không thay thế toàn bộ bản ghi người dùng. Phương thức HTTP nào phù hợp nhất?

3. Phương thức HTTP nào sau đây KHÔNG mang tính idempotent?

4. Client gửi một request POST để tạo resource mới và server xử lý thành công. Status code nào là phù hợp nhất để trả về?

5. Một người dùng đã đăng nhập nhưng cố gắng truy cập vào endpoint chỉ dành cho admin mà họ không có quyền sử dụng. Server nên trả về status code nào?

6. Header HTTP nào mà client nên gửi kèm để báo cho server biết rằng client mong đợi phản hồi dạng JSON?

7. Trong ví dụ về Express từ bài học, res.json() tự động thực hiện điều gì mà nếu không có nó bạn sẽ phải thiết lập thủ công?

Bài 1: Nền tảng REST và HTTP