DEV Community

JackTT
JackTT

Posted on

GraphQL là gì — Tại sao nên dùng cho Social App?

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
Enter fullscreen mode Exit fullscreen mode

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 } }
  }
}
Enter fullscreen mode Exit fullscreen mode

→ 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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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)