Bỏ qua đến nội dung chính
REST APIAPI designHTTPbackendweb service

REST API Là Gì? Cách Thiết Kế REST API Chuẩn 2026

REST API là gì và cách thiết kế chuẩn 2026: resource-oriented URL, HTTP method đúng, status code, versioning, pagination và error format dùng được.

Xuất bản 9 phút đọc

REST API là gì? Đây là câu hỏi gần như mọi backend developer đều phải trả lời ở phỏng vấn. Trong bài này em sẽ đi từ định nghĩa, các nguyên tắc cốt lõi, đến checklist thiết kế REST API đúng chuẩn 2026 — kèm ví dụ thực tế trong Node.js.

REST API là gì? Định nghĩa ngắn gọn

REST (Representational State Transfer) là kiến trúc phần mềm do Roy Fielding mô tả trong luận án năm 2000. REST API là API tuân thủ các ràng buộc REST: client–server, stateless, cacheable, uniform interface và layered system.

Trong thực tế, hầu hết "REST API" anh thấy chỉ tuân thủ một phần các ràng buộc trên — đặc biệt là HATEOAS gần như không được dùng. Quan trọng không phải "RESTful 100%", mà là API có dễ học, dễ dùng, dễ scale hay không.

6 nguyên tắc cốt lõi của REST API

Dưới đây là 6 ràng buộc REST. Anh không cần thuộc lòng — chỉ cần hiểu lý do tại sao mỗi ràng buộc giúp API dễ dùng hơn:

  • Client–Server: tách backend và frontend. Hai bên evolve độc lập.
  • Stateless: server không lưu session giữa request. Mỗi request tự chứa đủ thông tin.
  • Cacheable: response có thể cache (qua header). Giảm tải server, tăng tốc client.
  • Uniform Interface: dùng HTTP method và status code chuẩn. Client mọi loại đều hiểu.
  • Layered System: client không cần biết có proxy/CDN/load balancer ở giữa.
  • Code on Demand (optional): server có thể gửi code (JS) cho client thực thi.

Thiết kế URL theo resource

Quy tắc vàng: URL là danh từ, HTTP method là động từ. Đừng nhồi động từ vào URL.

❌ Sai
GET  /getUsers
POST /createUser
POST /deleteOrder?id=42
GET  /api/getUserOrdersByDate?userId=1&date=2026-05-01

✓ Đúng
GET    /users
POST   /users
DELETE /orders/42
GET    /users/1/orders?since=2026-05-01

Một vài quy ước nhỏ:

  • Dùng số nhiều cho collection: /users không phải /user
  • Nested resource max 2 cấp: /users/1/orders OK, /users/1/orders/2/items/3/reviews thì tách
  • Filter bằng query string: /orders?status=paid&limit=20
  • Action không phải CRUD: dùng sub-resource POST /orders/42/refund thay vì POST /refundOrder/42

HTTP method dùng đúng nghĩa

5 method anh sẽ dùng 95% thời gian:

GET    /users           → list users
GET    /users/42        → fetch user 42
POST   /users           → tạo user mới
PUT    /users/42        → replace toàn bộ user 42
PATCH  /users/42        → update một phần user 42
DELETE /users/42        → xoá user 42

Idempotency rất quan trọng: GET, PUT, DELETE phải idempotent — gọi 1 lần hay 10 lần cho cùng kết quả. POST không idempotent (mỗi lần tạo thêm record). PATCH có thể hoặc không, tuỳ implement.

Ví dụ Express:

app.get('/users', listUsers)
app.get('/users/:id', getUser)
app.post('/users', createUser)         // POST tạo mới
app.put('/users/:id', replaceUser)     // PUT thay toàn bộ
app.patch('/users/:id', updateUser)    // PATCH update partial
app.delete('/users/:id', deleteUser)
app.post('/users/:id/avatar', uploadAvatar) // sub-resource action

Status code: trả đúng ngay từ đầu

Trả status code đúng giúp client xử lý generic, monitoring tự động phát hiện error spike, CDN cache đúng. Không trả 200 + body lỗi — đó là chuẩn SOAP đời cũ.

200 OK              GET/PUT/PATCH thành công
201 Created         POST tạo mới
204 No Content      DELETE thành công, không trả body
400 Bad Request     payload sai (validation fail)
401 Unauthorized    chưa đăng nhập / token sai
403 Forbidden       đã đăng nhập nhưng không có quyền
404 Not Found       resource không tồn tại
409 Conflict        version mismatch / duplicate
422 Unprocessable   semantic invalid (ví dụ ngày sinh tương lai)
429 Too Many Requests  rate limit
500 Internal Server Error  bug server
503 Service Unavailable    overload, maintenance

Phân biệt 401 vs 403 hay nhầm: 401 = "chưa biết bạn là ai", 403 = "biết bạn là ai nhưng không cho phép".

Error format thống nhất

Không có chuẩn duy nhất nhưng RFC 7807 Problem Details là gần chuẩn nhất. Tối thiểu mỗi error response nên có:

{
  "error": {
    "code": "USER_EMAIL_TAKEN",
    "message": "Email này đã được đăng ký",
    "field": "email",
    "request_id": "req_8f3kd9"
  }
}
  • code: machine-readable, snake_case hoặc dot notation. Dùng để client switch logic.
  • message: human-readable, hiển thị được cho user (đã localize).
  • field: cho validation error, link với form field.
  • request_id: để dev tra log khi user phàn nàn.

Versioning: URL prefix là lựa chọn rõ ràng nhất

Có 3 cách version REST API:

  1. URL prefix: /v1/users, /v2/users. Rõ, dễ debug, dễ route ở proxy. Stripe, GitHub, Twilio đều dùng cách này.
  2. Accept header: Accept: application/vnd.alodev.v2+json. Sạch URL nhưng khó test bằng curl, khó cache.
  3. Query param: ?v=2. Tệ nhất — dễ quên, lẫn với filter.

Đề xuất: dùng URL prefix /v1/ cho mọi public API. Khi cần break change, mở /v2/ song song, deprecate v1 sau 6-12 tháng.

Pagination: cursor cho dataset lớn

Offset pagination dễ implement nhưng chậm khi page sâu:

-- Offset, page 1000, limit 50 → DB phải scan 50000 rows
SELECT * FROM orders ORDER BY id LIMIT 50 OFFSET 50000;

Cursor (keyset) pagination chỉ scan đúng số rows cần:

// API: GET /orders?limit=50&cursor=eyJpZCI6MTIzNDV9
const cursor = req.query.cursor ? JSON.parse(atob(req.query.cursor)) : null
const where = cursor ? 'WHERE id < $1' : ''
const rows = await db.query(`
  SELECT * FROM orders ${where}
  ORDER BY id DESC
  LIMIT 50
`, cursor ? [cursor.id] : [])

const nextCursor = rows.length === 50
  ? btoa(JSON.stringify({ id: rows[rows.length-1].id }))
  : null

res.json({ data: rows, next_cursor: nextCursor })

Client gọi tiếp với cursor nhận được — không cần biết "page number". Tham khảo thêm Index trong SQL để hiểu vì sao cursor hiệu quả hơn offset.

Checklist REST API ready production

  • ✅ URL theo resource, danh từ số nhiều, max 2 cấp nested
  • ✅ HTTP method và status code đúng nghĩa
  • ✅ Versioning bằng /v1/ prefix
  • ✅ Error format có code + message + request_id
  • ✅ Pagination dùng cursor cho dataset > 10k
  • ✅ OpenAPI spec cho mọi endpoint (Swagger UI cho dev test)
  • ✅ Rate limiting (xem bài Rate limiting)
  • ✅ Authentication chuẩn (xem JWT)
  • ✅ HTTPS only, không expose internal IP/error trace

Kết luận

Hiểu rõ REST API là gì giúp anh thiết kế interface mà cả frontend, mobile, partner đều dùng được dễ dàng. Đừng quá ám ảnh "RESTful tuyệt đối" — quan trọng là URL clean, method đúng, error rõ ràng, version có chiến lược. Đọc thêm GraphQL vs REST để cân nhắc lựa chọn cho dự án mới của anh.

Zalo