GraphQL là gì — Tại sao nên dùng cho Social App?
1. GraphQL là gì?
GraphQL là ngôn ngữ truy vấn cho API, do Facebook phát triển năm 2015. Thay vì server quyết định bạn nhận được gì, client tự chỉ định chính xác dữ liệu mình cần.
So sánh nhanh với REST
| REST | GraphQL | |
|---|---|---|
| Endpoint | Nhiều (/users, /posts...) |
Chỉ 1 (/graphql) |
| Dữ liệu trả về | Fixed, server quyết định | Linh hoạt, client quyết định |
| Over-fetching | Thường xuyên | Không bao giờ |
| Under-fetching | Phải gọi nhiều request | Lấy 1 lần được hết |
3 khái niệm cốt lõi
- Query — đọc dữ liệu (như GET)
- Mutation — thay đổi dữ liệu (như POST/PUT/DELETE)
- Subscription — lắng nghe realtime (như WebSocket)
2. Tại sao GraphQL phù hợp với Social App?
2.1 Một request — render cả trang
Feed social cần render cùng lúc: avatar, tên user, ảnh post, số like, top comment...
REST: 4–5 request tuần tự hoặc song song
GET /feed
GET /users/:id
GET /posts/:id/likes
GET /posts/:id/comments
GraphQL: 1 request duy nhất, server tự resolve
query Feed {
feed {
author { avatar name isFollowing }
content images
likes { count isLikedByMe }
topComments { text author { name } }
}
}
→ Giảm latency, giảm round-trip, render nhanh hơn rõ rệt.
2.2 Lấy đúng field cần — không thừa, không thiếu
Màn hình khác nhau → nhu cầu data khác nhau:
| Màn hình | Cần gì |
|---|---|
| Feed (thumbnail) |
name, avatar, title, likeCount
|
| Post detail | thêm fullContent, comments, shares
|
| Profile card | chỉ name, avatar, followerCount
|
REST trả về cùng 1 response nặng cho tất cả. GraphQL cho phép mỗi screen query đúng field nó cần — đặc biệt quan trọng với mobile, tiết kiệm bandwidth đáng kể.
2.3 Frontend không cần "gom" data từ nhiều API
REST — FE phải tự join và normalize:
const user = await getUser(id)
const posts = await getPosts(id)
const followers = await getFollowers(id)
// Rồi tự map, merge...
const profileData = mergeAndNormalize(user, posts, followers)
GraphQL — server chịu trách nhiệm resolve mọi relationship, FE nhận về 1 object sẵn sàng dùng. Logic mapping nằm ở tầng resolver thay vì rải rác ở từng màn hình.
2.4 Caching & Batching — xử lý data lặp lại thông minh
Social feed hay có pattern: cùng 1 user xuất hiện ở nhiều post.
DataLoader pattern tự động batch + deduplicate:
Post 1 → author_id: 42 ┐
Post 2 → author_id: 17 ├─ DataLoader → 1 DB query duy nhất
Post 3 → author_id: 42 ┘ SELECT * FROM users WHERE id IN (42, 17)
Client-side (Apollo/urql) cũng normalize cache theo object ID — user cập nhật avatar ở 1 chỗ, toàn bộ feed tự cập nhật, không cần refetch.
2.5 Self-documenting — FE tự khám phá API
GraphQL có Introspection tích hợp sẵn: schema chính là documentation. Công cụ như GraphiQL / Apollo Sandbox cho phép:
- Autocomplete field khi gõ query
- Xem type, mô tả của từng field ngay trong IDE
- Không cần Postman collection hay Swagger riêng
→ FE và BE làm việc từ cùng 1 source of truth — schema file. Thêm field mới, FE thấy ngay.
3. Pain Point thực tế: /api/auth/me
Đây là ví dụ điển hình về vấn đề REST gặp phải trong production.
Hiện trạng — trả về "tất cả hoặc không có gì"
// GET /api/auth/me
{
"id": 123456,
"name": "Nguyễn Văn A",
"avatar": "https://...",
"coin": 45200,
"rank": "GOLD", // ❌ aggregate loyalty_histories
"rankProgress": 78.5, // ❌ tính toán nặng
"totalEarned": 1250000, // ❌ SUM toàn bộ lịch sử
"totalSpent": 890000, // ❌ SUM toàn bộ lịch sử
"referralCount": 12, // ❌ COUNT từ bảng referrals
"taskCompletedCount": 47, // ❌ COUNT từ bảng task_points
"badges": [...] // ❌ JOIN thêm bảng badges
}
Mỗi lần app load → gọi /auth/me → server tính toán tất cả, dù màn hình chỉ cần hiện tên + avatar.
Phân tích chi phí thực tế
| Field | Chi phí server | Tần suất FE dùng |
|---|---|---|
name, avatar, coin
|
Rất thấp | Mọi màn hình |
rank, rankProgress
|
Nặng — aggregate loyalty_histories
|
Chỉ màn hình Profile/Rank |
totalEarned, totalSpent
|
Nặng — SUM toàn lịch sử | Chỉ màn hình Thống kê |
referralCount |
Trung bình | Chỉ màn hình Referral |
taskCompletedCount |
Trung bình | Chỉ màn hình Task |
badges |
Nặng — multi JOIN | Chỉ màn hình Profile |
→ 90% request /auth/me chỉ cần 3–4 field đầu, nhưng vẫn phải trả tiền tính toán toàn bộ.
Với GraphQL — lazy computation theo từng màn hình
App header (render mọi lúc):
query AppHeader {
me {
name
avatar
coin # query cực nhẹ, chỉ SELECT 3 fields
}
}
Màn hình Profile/Rank (user chủ động vào):
query ProfileScreen {
me {
name avatar coin
rank # chỉ tính khi user thực sự mở màn hình này
rankProgress
badges
}
}
Màn hình Thống kê (vào ít nhất):
query StatsScreen {
me {
totalEarned # heavy computation chỉ chạy khi cần
totalSpent
referralCount
taskCompletedCount
}
}
Kết quả
| REST hiện tại | GraphQL | |
|---|---|---|
| App header load | ~150ms (tính toán full) | ~8ms (chỉ SELECT 3 fields) |
| Profile/Rank | ~150ms (dù không cần) | ~120ms (lazy, chỉ khi vào) |
| Server load (600K users) | Tính rank cho tất cả | Chỉ tính khi được query |
Heavy field như rank trở thành lazy resolver — không tốn tài nguyên tính toán cho 600K user mỗi khi họ mở app.
Top comments (0)