Khi bắt đầu dự án mới, câu hỏi GraphQL vs REST sẽ xuất hiện sớm hay muộn. Bài này phân tích thực tế (không hype) để giúp anh quyết: dự án nào nên GraphQL, dự án nào REST đủ tốt.
Bối cảnh ngắn gọn
REST có từ 2000 (Roy Fielding). GraphQL ra đời 2015 tại Facebook để giải bài toán mobile của họ: app iOS/Android cần shape data khác web, REST endpoint mỗi case làm phình backend. GraphQL cho phép client tự định shape, server chỉ define schema và resolver.
Sự khác biệt cốt lõi
REST GraphQL
URL endpoint /users/1/orders POST /graphql (luôn)
HTTP method GET/POST/PUT/PATCH/DEL POST (gần như luôn)
Shape data Server quyết Client query đúng field
Versioning /v1, /v2 Schema evolution (deprecate)
Caching HTTP cache (CDN) Client cache (Apollo)
Tooling Postman, OpenAPI GraphiQL, Apollo Studio
Learning curve Thấp Trung bình
Vấn đề over-fetching và under-fetching
Đây là lý do số 1 team migrate sang GraphQL.
Over-fetching: client cần 3 field nhưng REST trả 30 field. Mobile lãng phí bandwidth.
// REST
GET /users/1
→ { id, name, email, phone, address, bio, avatar, ... 25 field khác }
// GraphQL — client xin đúng field
query { user(id: 1) { name avatar } }
→ { user: { name: "An", avatar: "..." } }
Under-fetching: client cần data từ 3 endpoint → 3 round trip.
// REST: 3 request
GET /users/1
GET /users/1/orders
GET /orders/last/items
// GraphQL: 1 request
query {
user(id: 1) {
name
orders { id total items { name price } }
}
}
GraphQL N+1 — đừng quên DataLoader
GraphQL nested query rất dễ tạo N+1. Khi client query 50 users + orders của mỗi user, naive resolver sẽ chạy 1 query lấy users + 50 query lấy orders.
// ❌ Sai — 51 query
const resolvers = {
User: {
orders: async (user) => await db.query(
'SELECT * FROM orders WHERE user_id = ?', [user.id])
}
}
// ✓ Đúng — DataLoader batch
import DataLoader from 'dataloader'
const ordersLoader = new DataLoader(async (userIds) => {
const rows = await db.query(
'SELECT * FROM orders WHERE user_id IN (?)', [userIds])
// Group by user_id để return theo đúng thứ tự userIds
return userIds.map(id => rows.filter(r => r.user_id === id))
})
const resolvers = {
User: { orders: (user) => ordersLoader.load(user.id) }
}
// → 2 query: 1 cho users + 1 cho tất cả orders
Tham khảo bài Tối ưu Database phần "diệt N+1" để hiểu sâu hơn.
Caching: chỗ REST mạnh hơn rõ rệt
REST GET request có thể cache ở:
- Browser (Cache-Control)
- CDN (Cloudflare, Fastly)
- Reverse proxy (Varnish, nginx)
- Service worker
GraphQL POST + variable body không cache được trực tiếp ở HTTP layer. Cần:
- Client cache: Apollo Client, Relay (theo entity ID)
- Server cache: response cache plugin theo query hash
- Persisted queries: gán mỗi query một ID, GET request với ID → có thể HTTP cache
Nếu app anh phụ thuộc nặng CDN cache (e.g. blog public, e-commerce listing), REST đơn giản hơn nhiều.
Security: GraphQL cần thêm guardrail
REST attacker chỉ có thể gọi endpoint anh expose. GraphQL attacker có thể compose query bất kỳ — nguy hiểm hơn:
# Query attack — nested 10 cấp, mỗi cấp resolve hàng nghìn row
query Attack {
users {
orders {
items {
product {
reviews {
user {
orders { items { product { ... } } }
}
}
}
}
}
}
}
Bắt buộc với public GraphQL:
- Query depth limit (max 7-10)
- Query complexity scoring
- Rate limiting theo complexity, không phải request count
- Persisted queries cho production client (chỉ cho phép query đã đăng ký)
Tooling và developer experience
GraphQL: GraphiQL/Apollo Studio cho phép explore schema, autocomplete query. Client (Apollo, urql, Relay) có codegen TypeScript types từ schema → end-to-end type safety.
REST: OpenAPI/Swagger spec → Swagger UI explore, codegen client. Postman cực mạnh. Tooling REST trưởng thành hơn 20+ năm, gần như mọi ngôn ngữ đều có client tốt.
Khi nào nên chọn GraphQL?
- ≥3 client với data shape khác nhau (web, iOS, Android, public partner)
- Schema phức tạp, nhiều entity quan hệ (e-commerce, social network)
- Mobile bandwidth là vấn đề thật
- Team đã có kinh nghiệm GraphQL hoặc sẵn sàng đầu tư học
- Backend là federation của nhiều microservice (Apollo Federation)
Khi nào REST đủ tốt?
- 1-2 client cùng team viết
- Schema đơn giản, ít quan hệ nested
- Cần CDN cache mạnh (public content)
- Team mới bắt đầu, ưu tiên ship sớm
- Tích hợp với partner B2B (REST + OpenAPI dễ chia sẻ)
Phần lớn dự án vừa và nhỏ — REST là lựa chọn an toàn. Đọc thêm REST API là gì để build đúng từ đầu.
Phương án hybrid: cả hai
Nhiều công ty lớn (GitHub, Shopify, Netflix) dùng cả REST và GraphQL:
- REST cho public partner API (ổn định, dễ tích hợp)
- GraphQL cho internal frontend (linh hoạt, tốc độ)
- REST cho webhook, file upload
- GraphQL cho dashboard phức tạp
Kết luận
GraphQL vs REST không có winner tuyệt đối. REST đủ cho 80% dự án vừa và nhỏ. GraphQL toả sáng khi schema phức tạp + nhiều client. Quyết định dựa vào nhu cầu thực tế chứ không hype. Nếu phân vân, bắt đầu bằng REST — migrate sau cũng không quá đắt nếu URL/error format đã chuẩn.