Bạn vừa nhận task đầu tiên là xây dựng API RESTful cho dự án nhưng không biết bắt đầu từ đâu? Hoặc API hiện tại của team đang lộn xộn, mỗi endpoint một kiểu, frontend than phiền liên tục? Bài viết này sẽ hướng dẫn bạn cách xây dựng RESTful API chuẩn từ con số 0, theo đúng best practice mà các công ty công nghệ lớn như Stripe, GitHub, Twilio đang áp dụng. Bạn sẽ học được 10 nguyên tắc cốt lõi của REST API design, từ cách đặt tên endpoint, sử dụng HTTP methods đúng chuẩn, xử lý lỗi, versioning, đến security và documentation. Mỗi nguyên tắc đều có ví dụ code thực tế bằng Node.js Express để bạn áp dụng ngay vào dự án. Dù bạn dùng Python, Java hay Go, các nguyên tắc này đều áp dụng được. Cùng bắt đầu nào!
RESTful API là gì và vì sao phải xây dựng theo chuẩn?
REST (Representational State Transfer) là một kiến trúc thiết kế API được Roy Fielding đề xuất năm 2000 trong luận án tiến sĩ của ông. RESTful API là API tuân theo các nguyên tắc của REST, sử dụng HTTP protocol để giao tiếp giữa client và server thông qua các tài nguyên (resources) được định danh bằng URL.
Một API được xây dựng đúng chuẩn REST mang lại nhiều lợi ích thực tế: dễ hiểu cho dev mới onboard, dễ document, dễ test, scale tốt, và quan trọng nhất là frontend dev sẽ "yêu" bạn vì không phải đoán mò ý đồ của backend. Ngược lại, một API không chuẩn sẽ là cơn ác mộng maintain, mỗi thay đổi nhỏ đều có nguy cơ phá tung mọi thứ.
10 nguyên tắc xây dựng RESTful API chuẩn
Dưới đây là 10 nguyên tắc bạn cần nắm vững. Mình sắp xếp theo thứ tự ưu tiên, từ cơ bản nhất đến nâng cao, để bạn áp dụng dần khi code.
1. Sử dụng danh từ số nhiều cho resource (Năm áp dụng: vĩnh viễn)
Nguyên tắc: Resource trong REST đại diện cho một "thứ" (entity), nên dùng danh từ thay vì động từ. Và luôn dùng dạng số nhiều để nhất quán.
Ví dụ đúng:
GET /users // Lấy danh sách users
GET /users/123 // Lấy user có id 123
POST /users // Tạo user mới
PUT /users/123 // Update toàn bộ user 123
DELETE /users/123 // Xóa user 123
Ví dụ sai (cần tránh):
GET /getUser?id=123 // Sai: dùng động từ getUser
POST /createUser // Sai: dùng động từ createUser
GET /user/123 // Sai: dùng số ít user thay vì users
Best practice: Endpoint phải đọc lên là hiểu ngay nó làm gì, không cần đọc docs.
2. Dùng đúng HTTP Methods cho từng hành động
Nguyên tắc: Mỗi HTTP method có mục đích riêng. Đừng dùng GET để xóa data hay POST để query.
- GET: Lấy dữ liệu. Phải idempotent (gọi nhiều lần kết quả như nhau) và safe (không thay đổi data).
- POST: Tạo resource mới. Không idempotent (gọi 2 lần có thể tạo 2 record).
- PUT: Update toàn bộ resource. Idempotent.
- PATCH: Update một phần resource. Có thể idempotent hoặc không.
- DELETE: Xóa resource. Idempotent (xóa 1 lần hay 10 lần kết quả vẫn là không tồn tại).
Ví dụ code Express.js:
app.get('/api/users', getAllUsers);
app.get('/api/users/:id', getUserById);
app.post('/api/users', createUser);
app.put('/api/users/:id', updateUser);
app.patch('/api/users/:id', partialUpdateUser);
app.delete('/api/users/:id', deleteUser);
3. Sử dụng HTTP Status Code đúng nghĩa
Nguyên tắc: Đừng trả về 200 OK cho mọi response rồi nhét lỗi vào body. HTTP status code có ý nghĩa rõ ràng, hãy dùng đúng.
Các status code thường gặp:
- 200 OK: Request thành công (GET, PUT, PATCH).
- 201 Created: Resource mới đã được tạo (POST).
- 204 No Content: Thành công nhưng không có body trả về (DELETE).
- 400 Bad Request: Request không hợp lệ (validation fail).
- 401 Unauthorized: Chưa đăng nhập hoặc token không hợp lệ.
- 403 Forbidden: Đã đăng nhập nhưng không có quyền.
- 404 Not Found: Resource không tồn tại.
- 409 Conflict: Conflict với state hiện tại (vd: email đã tồn tại).
- 422 Unprocessable Entity: Cú pháp đúng nhưng không xử lý được.
- 500 Internal Server Error: Lỗi server, không phải lỗi client.
4. Versioning API ngay từ đầu
Nguyên tắc: Đừng đợi đến khi có breaking change mới nghĩ đến versioning. Đặt v1 ngay từ ngày đầu để tránh đau đầu sau này.
3 cách versioning phổ biến:
- URL versioning (phổ biến nhất):
/api/v1/usersrồi/api/v2/users. - Header versioning: Truyền version qua header
Accept: application/vnd.api.v1+json. - Query parameter:
/api/users?version=1(ít dùng vì xấu).
Khuyến nghị: Dùng URL versioning vì dễ hiểu, dễ debug trên Postman, và search log dễ.
5. Pagination, Filtering, Sorting cho danh sách
Nguyên tắc: Đừng bao giờ trả về toàn bộ records trong một request. Database 1 triệu rows mà bạn trả hết là sập server ngay.
Pagination chuẩn:
GET /api/v1/users?page=1&limit=20
GET /api/v1/users?offset=0&limit=20 // Cách khác
GET /api/v1/users?cursor=eyJpZCI6MTAwfQ // Cursor-based, tốt cho data lớn
Filtering:
GET /api/v1/users?role=admin&status=active
GET /api/v1/products?price_min=100&price_max=500
Sorting:
GET /api/v1/users?sort=createdAt&order=desc
GET /api/v1/users?sort=-createdAt // Cách viết tắt: dấu "-" là desc
6. Response Format nhất quán
Nguyên tắc: Mọi response từ API phải có format thống nhất. Frontend dev không phải đoán mỗi endpoint trả về cấu trúc gì.
Format đề xuất:
{
"success": true,
"data": {
"id": 123,
"name": "Nguyen Van A",
"email": "[email protected]"
},
"message": "User retrieved successfully",
"meta": {
"timestamp": "2026-04-23T10:30:00Z",
"version": "1.0"
}
}
Format cho list có pagination:
{
"success": true,
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 458,
"totalPages": 23
}
}
7. Error Handling rõ ràng và informative
Nguyên tắc: Khi lỗi xảy ra, response phải nói rõ lỗi gì, ở đâu, làm sao fix. Đừng trả về "Error" rồi để frontend tự đoán.
Error format chuẩn:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Email không hợp lệ",
"details": [
{
"field": "email",
"issue": "must be a valid email address",
"value": "abc.com"
}
]
},
"meta": {
"timestamp": "2026-04-23T10:30:00Z",
"requestId": "req_abc123"
}
}
Lưu ý quan trọng: Đừng leak thông tin nhạy cảm như stack trace, query SQL, file path trong production. Log những thứ đó vào server log, response chỉ trả message thân thiện.
8. Authentication và Authorization
Nguyên tắc: 99% API thực tế cần xác thực người dùng. Đừng tự nghĩ ra schema mới, dùng các chuẩn đã được chứng minh.
Các phương thức phổ biến:
- JWT (JSON Web Token): Phổ biến nhất, stateless, scale tốt. Phù hợp cho SPA và mobile app.
- OAuth 2.0: Khi cần cho bên thứ 3 truy cập (Login with Google, Facebook).
- API Key: Cho server-to-server hoặc API public có rate limit.
- Session-based: Truyền thống, phù hợp web app monolith.
Header chuẩn cho JWT:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
9. Rate Limiting và Security Headers
Nguyên tắc: API không có rate limit là lời mời gọi DDoS và abuse. Mọi API public đều cần giới hạn số request mỗi user trong khoảng thời gian.
Response headers nên có:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1714028400
Khi vượt limit, trả về 429 Too Many Requests:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Try again in 60 seconds."
}
}
Security headers bắt buộc:
- HTTPS only: Đừng bao giờ deploy API HTTP trong production.
- CORS configuration: Whitelist origin cụ thể, đừng dùng
*trừ public API. - Helmet.js (Node.js): Tự động set các security headers.
- Input validation: Dùng thư viện như Joi, Zod để validate mọi input.
10. Documentation đầy đủ với OpenAPI / Swagger
Nguyên tắc: API không có docs là API chết. Frontend dev sẽ phải hỏi backend mỗi 5 phút, hoặc tệ hơn, tự đoán rồi dẫn đến bug production.
Các tool documentation phổ biến:
- Swagger / OpenAPI 3.0: Tiêu chuẩn công nghiệp, có UI tương tác để test luôn.
- Postman Collections: Chia sẻ collection cho team, có example request/response.
- Insomnia: Alternative của Postman, miễn phí và nhẹ hơn.
- ReadMe, Stoplight: Giải pháp commercial cho team lớn.
Nội dung docs nên có: Mô tả endpoint, params, request body example, response example (success và error), authentication required, rate limit, version history.
Code mẫu: Xây dựng RESTful API hoàn chỉnh với Node.js Express
Để bạn dễ hình dung cách kết hợp 10 nguyên tắc trên, đây là một code mẫu hoàn chỉnh cho resource Users.
Cấu trúc thư mục đề xuất
src/
├── config/
│ └── database.js
├── controllers/
│ └── userController.js
├── middlewares/
│ ├── auth.js
│ ├── errorHandler.js
│ ├── rateLimiter.js
│ └── validator.js
├── models/
│ └── User.js
├── routes/
│ └── v1/
│ └── users.js
├── utils/
│ └── responseFormatter.js
└── app.js
Ví dụ Controller áp dụng đầy đủ best practice
// controllers/userController.js
const User = require('../models/User');
const { successResponse, errorResponse } = require('../utils/responseFormatter');
exports.getAllUsers = async (req, res, next) => {
try {
const { page = 1, limit = 20, sort = '-createdAt', role } = req.query;
const filter = {};
if (role) filter.role = role;
const users = await User.find(filter)
.sort(sort)
.skip((page - 1) * limit)
.limit(parseInt(limit));
const total = await User.countDocuments(filter);
return res.status(200).json({
success: true,
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
totalPages: Math.ceil(total / limit)
}
});
} catch (error) {
next(error);
}
};
exports.createUser = async (req, res, next) => {
try {
const user = await User.create(req.body);
return res.status(201).json({
success: true,
data: user,
message: 'User created successfully'
});
} catch (error) {
if (error.code === 11000) {
return res.status(409).json({
success: false,
error: {
code: 'DUPLICATE_EMAIL',
message: 'Email already exists'
}
});
}
next(error);
}
};
Checklist trước khi deploy API lên production
Trước khi push API ra production, hãy review lại checklist này. Mình từng trả giá đắt vì bỏ qua vài điểm trong list.
- Tất cả endpoint đều dùng HTTPS, không HTTP.
- Authentication đã được implement đúng cách (JWT, OAuth, API key).
- Rate limiting đã được áp dụng cho mọi endpoint public.
- Input validation cho mọi request body, query, params.
- Error response không leak stack trace hoặc thông tin nhạy cảm.
- Logging đầy đủ (requestId, user, endpoint, response time).
- Pagination đã có cho mọi endpoint trả về list.
- CORS configuration đã whitelist domain cụ thể.
- API documentation đã có và update mới nhất.
- Có versioning trong URL (v1, v2...).
- Health check endpoint
/healthhoạt động. - Database connection có retry logic và pooling.
- Environment variables không hardcode trong code.
- Có monitoring (Sentry, Datadog, NewRelic) để track error.
Các sai lầm thường gặp khi xây dựng RESTful API
Sau nhiều năm review code và làm mentor, mình tổng hợp các lỗi phổ biến nhất mà junior dev hay mắc phải.
- Dùng GET cho hành động thay đổi data: Ví dụ
GET /deleteUser/123. Sai vì GET phải safe và idempotent. - Trả về 200 OK cho mọi response: Kể cả khi có lỗi. Frontend không biết phân biệt thành công hay thất bại nếu chỉ nhìn status.
- Đặt tên endpoint kiểu hàm:
/getUserList,/saveUser. Đúng phải là/users. - Không versioning: Lúc cần breaking change phải khổ sở migration cho mọi client.
- Trả response khổng lồ: Một endpoint trả 5000 records không pagination. Sập server là chuyện sớm muộn.
- Hardcode credentials: Database password, API key, JWT secret để trong code và push lên Git.
- Không log requestId: Khi có bug, không biết request nào gây ra, debug như mò kim đáy bể.
Câu hỏi thường gặp (FAQ)
RESTful API và REST API có khác nhau không?
Hai thuật ngữ này thường được dùng thay thế nhau và về cơ bản là giống nhau. "REST API" là tên gọi chung cho API sử dụng kiến trúc REST, còn "RESTful API" nhấn mạnh việc API đó tuân thủ đầy đủ các nguyên tắc REST. Trong thực tế, rất ít API "thuần RESTful" 100% (theo định nghĩa Roy Fielding gốc cần đạt level 4 với HATEOAS), đa số là "REST-like API" tuân thủ phần lớn nguyên tắc.
Khi nào nên dùng PUT và khi nào nên dùng PATCH?
Dùng PUT khi bạn muốn replace toàn bộ resource bằng data mới (gửi lên đầy đủ field, field nào không gửi sẽ bị set về null hoặc default). Dùng PATCH khi chỉ muốn update một vài field cụ thể (chỉ gửi field cần thay đổi). Trong thực tế, PATCH được dùng nhiều hơn vì hiệu quả về băng thông và linh hoạt hơn cho frontend.
Có nên dùng GraphQL thay cho REST không?
Phụ thuộc use case. GraphQL phù hợp khi bạn có nhiều client với nhu cầu data khác nhau (web, mobile, IoT), hoặc data có quan hệ phức tạp. REST phù hợp cho hầu hết các use case CRUD đơn giản, dễ cache, dễ debug, ecosystem mature hơn. Nhiều công ty lớn dùng cả hai song song: REST cho public API và GraphQL cho internal.
Học xây dựng RESTful API mất bao lâu?
Để hiểu cơ bản và build được API CRUD đơn giản, khoảng 2 đến 4 tuần với người đã biết một ngôn ngữ backend. Để thành thạo các nguyên tắc nâng cao như authentication, caching, rate limiting, security, cần khoảng 3 đến 6 tháng làm việc thực tế trên dự án. Để đạt level senior với khả năng design API system phức tạp, scale lớn, thường cần 2 đến 3 năm kinh nghiệm.
Framework nào tốt nhất để xây dựng RESTful API?
Không có framework "tốt nhất" tuyệt đối, mỗi framework có thế mạnh riêng. Phổ biến nhất hiện nay: Express.js và NestJS (Node.js), FastAPI và Django REST Framework (Python), Spring Boot (Java), Laravel (PHP), Gin (Go), ASP.NET Core (C#). Lời khuyên: Chọn ngôn ngữ team bạn mạnh nhất, framework chỉ là công cụ.
Kết luận
10 nguyên tắc trên là nền tảng để bạn xây dựng API RESTful chuẩn cho mọi dự án từ MVP đến enterprise. Nhớ rằng RESTful API tốt không phải là API có tính năng nhiều nhất, mà là API dễ hiểu, dễ dùng, dễ maintain. Frontend dev và mobile dev sẽ là người làm việc với API của bạn hàng ngày, hãy đối xử với họ như khách hàng. Bắt đầu từ việc đặt tên endpoint cho đúng, dùng HTTP methods và status code đúng nghĩa, rồi dần áp dụng các best practice nâng cao như versioning, rate limiting, documentation. Practice REST API design qua các side project nhỏ trước khi áp dụng vào production. Quan trọng nhất: hãy đọc API docs của các công ty lớn như Stripe, GitHub, Twilio để học cách họ design, đó là tài liệu học tốt nhất bạn có thể tìm.
Ý kiến của bạn 0
Chưa có bình luận nào. Hãy là người đầu tiên!