← Về danh sách
Kỹ nghệ & Quản lý phần mềm
#GraphQL#API#스키마#리졸버#REST비교
Cập nhật lần cuối · 2026-08-25

GraphQL (ngôn ngữ truy vấn API)

1. Tổng quan

A. Định nghĩa

GraphQL là ngôn ngữ truy vấn (query language) dành cho API đồng thời là đặc tả thực thi runtime, trong đó client tự chỉ rõ cấu trúc dữ liệu cần thiết khi yêu cầu, và máy chủ trả về phản hồi đúng theo hình dạng (shape) đó. GraphQL được Meta (trước đây là Facebook) công bố năm 2015, và hiện nay đặc tả do một quỹ trung lập (GraphQL Foundation) quản lý.

Ý tưởng cốt lõi của GraphQL nằm ở việc đảo ngược cấu trúc API truyền thống "client nhận phản hồi mà máy chủ đã định sẵn". Trong khi REST đặt endpoint cho mỗi tài nguyên (resource) và máy chủ quyết định hình dạng phản hồi, GraphQL đặt schema định kiểu mạnh (schema) tại một endpoint duy nhất (thường là /graphql), và client chỉ kết hợp các trường mình muốn bên trong schema đó để truy vấn. Kết quả là phản hồi trả về dưới dạng JSON khớp chính xác với các trường đã yêu cầu. Tức là quyền kiểm soát "lấy cái gì và lấy như thế nào" chuyển từ máy chủ sang client.

Vì vậy GraphQL không phải là một giao thức đơn thuần mà phải được hiểu như một công nghệ lấy hợp đồng (contract) làm trung tâm, quy định đồng thời hệ thống kiểu, ngôn ngữ truy vấn, engine thực thi. Schema chính là đặc tả đồng thời là tài liệu của API, và frontend với backend có thể lấy schema này làm hợp đồng chung để phát triển độc lập.

B. Bối cảnh ra đời và sự cần thiết

GraphQL ra đời từ ý thức vấn đề cụ thể là sự lan rộng của môi trường di động. Nhiều màn hình (iOS, Android, web) dùng cùng một backend nhưng dữ liệu cần cho mỗi màn hình hơi khác nhau, và nếu hỗ trợ điều này bằng REST sẽ phát sinh hai sự kém hiệu quả kinh niên. Thứ nhất là lấy thừa (over-fetching): dù chỉ cần một tên người dùng, máy chủ vẫn trả nguyên cả đối tượng người dùng đã định sẵn (địa chỉ, ngày đăng ký, thiết lập...). Thứ hai là lấy thiếu (under-fetching): để vẽ một màn hình phải gọi nhiều lần riêng rẽ các endpoint người dùng, đơn hàng, sản phẩm. Đặc biệt trong môi trường độ trễ lớn và băng thông hạn chế như mạng di động, chi phí khứ hồi (round-trip) này làm giảm mạnh hiệu năng cảm nhận của người dùng.

Một khó khăn khác mà REST gặp phải là bùng nổ endpoint và quản lý phiên bản. Mỗi khi yêu cầu màn hình tăng, các endpoint tùy biến như /user/{id}/summary, /user/{id}/detail tăng lên, và muốn thay đổi cấu trúc phản hồi thì phải rẽ nhánh phiên bản toàn bộ API theo kiểu /v1, /v2. Trong GraphQL, client chọn trường nên dù màn hình thay đổi cũng không cần thêm endpoint máy chủ, và thay đổi mang tính tiến hóa như thêm trường mới không làm hỏng client hiện có, cho phép tiến hóa không phiên bản (versionless).

Tóm lại, GraphQL là công nghệ đồng thời nhắm đến bốn nhu cầu: ① tối thiểu hóa khứ hồi mạng và payload, ② đáp ứng yêu cầu khác nhau của nhiều loại client, ③ phát triển tách biệt frontend và backend thông qua hợp đồng định kiểu mạnh, ④ tự tài liệu hóa dựa trên schema. Tuy nhiên, sự linh hoạt này kéo theo cái giá là độ phức tạp phía máy chủ và gánh nặng về caching, bảo mật, nên việc áp dụng phải dựa trên đánh giá về sự đánh đổi.

2. Kiến trúc và cấu trúc thực thi của GraphQL

Hệ thống GraphQL xử lý tài liệu truy vấn mà client gửi theo luồng máy chủ phân tích cú pháp→kiểm tra→thực thi→phản hồi. Trung tâm của việc thực thi là schema và resolver (resolver), hàm thực sự tính giá trị của từng trường. Truy vấn của client có dạng cây, và máy chủ đi theo cây này từ trên xuống dưới, gọi resolver của từng trường để điền giá trị.

flowchart LR
    C["Client (web·di động)"] -->|"Truy vấn (Query/Mutation)"| GW["Máy chủ GraphQL (endpoint duy nhất)"]
    subgraph GW
      P["Phân tích·kiểm tra (đối chiếu schema)"] --> EX["Engine thực thi (gọi resolver)"]
    end
    EX --> R1["Resolver: User"]
    EX --> R2["Resolver: Orders"]
    R1 --> D1[("DB người dùng")]
    R2 --> D2[("Dịch vụ/DB đơn hàng")]
    EX -->|"JSON đúng hình dạng yêu cầu"| C

Trong cấu trúc trên, máy chủ tích hợp (aggregation) nhiều nguồn (DB, microservice, API bên ngoài) phía sau một schema duy nhất. Client không cần biết dữ liệu đến từ đâu mà chỉ yêu cầu các trường mà schema đã cam kết, còn resolver của từng trường gọi nguồn thích hợp ở phía sau. Ở điểm này, máy chủ GraphQL thường kiêm vai trò API gateway kiêm tầng kết hợp (BFF, Backend For Frontend) gom nhiều backend lại.

Các thao tác (operation) mà GraphQL quy định có ba loại. Query là truy vấn dữ liệu (đọc) và không được có tác dụng phụ, Mutation là thay đổi dữ liệu (ghi) làm thay đổi trạng thái máy chủ, còn Subscription là thao tác client liên tục nhận sự kiện từ máy chủ (đẩy thời gian thực, thường dựa trên WebSocket). Bảng dưới đây so sánh về khái niệm ba thao tác với tương ứng trong REST, nhưng khác biệt bản chất là trong GraphQL tất cả đều được xử lý qua cùng một endpoint duy nhất.

Thao tác Mục đích Tác dụng phụ Khái niệm tương tự REST
Query Truy vấn (đọc) Không (lũy đẳng) GET
Mutation Tạo·sửa·xóa Có (thay đổi trạng thái) POST/PUT/PATCH/DELETE
Subscription Đăng ký sự kiện thời gian thực Nhận luồng WebSocket/SSE

3. Thành phần cốt lõi — Schema, kiểu, resolver

Bộ khung của GraphQL là schema định kiểu mạnh được mô tả bằng ngôn ngữ định nghĩa schema (SDL, Schema Definition Language). Schema là bản hợp đồng khai báo các kiểu và trường mà API cung cấp, xác định chắc chắn client có thể hỏi gì và nhận câu trả lời dưới hình dạng nào. Chẳng hạn, được định nghĩa như sau.

type User {
  id: ID!
  name: String!
  orders: [Order!]!
}
type Order { id: ID! amount: Int! }
type Query { user(id: ID!): User }

Ở đây ! có nghĩa là không được null (non-null), [ ] có nghĩa là danh sách. Vì kiểu được chỉ rõ như vậy, máy chủ có thể thực hiện kiểm tra tĩnh bằng cách đối chiếu với schema trước khi thực thi yêu cầu, và công cụ phía client tải schema về để cung cấp tự động hoàn thành, sinh kiểu, tài liệu hóa. Nhờ chức năng introspection (introspection) cho phép truy vấn chính schema, API GraphQL tự mô tả bản thân mà không cần tài liệu riêng.

Resolver là hàm định nghĩa mỗi trường của schema thực sự trả về giá trị gì. Engine thực thi duyệt cây truy vấn và điền giá trị theo cách chuyển kết quả của trường cha cho resolver con. Sự thực thi phân cấp này vừa là sức mạnh vừa là nguồn rủi ro của GraphQL; ví dụ, nếu truy vấn danh sách người dùng rồi lại truy vấn đơn hàng của từng người, khi có N người dùng sẽ phát sinh thêm N truy vấn đơn hàng — vấn đề N+1. Nếu bỏ mặc, một màn hình có thể gây ra hàng trăm truy vấn DB.

Giải pháp chuẩn cho vấn đề này là mẫu DataLoader. Các yêu cầu truy vấn riêng lẻ phát sinh trong cùng một chu kỳ thực thi được gom lại trong chốc lát (batching) để xử lý gộp thành một truy vấn, và kết quả cho cùng khóa được lưu cache (caching). Ví dụ, thay vì truy vấn riêng đơn hàng của 100 người dùng, xử lý một lần bằng WHERE user_id IN (...) để giảm số truy vấn từ 101 lần xuống 2 lần. Như vậy, để gánh được sự linh hoạt của GraphQL trong thực tế, chiến lược batch và cache gần như trở thành thành phần bắt buộc.

sequenceDiagram
    participant C as Client
    participant S as Máy chủ GraphQL
    participant DL as DataLoader
    participant DB as Cơ sở dữ liệu
    C->>S: "Truy vấn users { name orders { amount } }"
    S->>DB: Truy vấn danh sách người dùng 1 lần
    S->>DL: "Yêu cầu orders của từng người dùng (riêng lẻ)"
    DL->>DB: "Gộp bằng mệnh đề IN, truy vấn 1 lần (batching)"
    DB-->>DL: Trả kết quả đơn hàng
    DL-->>S: Phân phối theo từng người dùng
    S-->>C: Phản hồi JSON đúng hình dạng yêu cầu

4. So sánh với REST — lý do tạo ra khác biệt và hàm ý thực tiễn

So sánh hơn kém đơn giản giữa GraphQL và REST là không thích hợp; cốt lõi theo góc nhìn Kỹ sư chuyên nghiệp (Professional Engineer) là hiểu khác biệt bắt nguồn từ đâu. Điểm phân nhánh căn bản của hai cách là "ai nắm quyền quyết định hình dạng phản hồi". REST cố định biểu diễn tài nguyên ở máy chủ nên có lợi về caching, bảo mật, đường cong học tập, nhưng khi sự đa dạng của client tăng thì khó tránh lấy thừa/thiếu và bùng nổ endpoint. GraphQL để client quyết định hình dạng nên hiệu quả mạng và năng suất frontend cao, nhưng cái giá là độ phức tạp máy chủ, độ khó caching và rủi ro lạm dụng truy vấn tăng lên.

Phân loại REST GraphQL
Endpoint Nhiều, theo tài nguyên Endpoint duy nhất
Lấy dữ liệu Máy chủ quyết định hình dạng (lấy thừa/thiếu) Client chọn trường
Hợp đồng kiểu Đặc tả riêng (OpenAPI...) Tích hợp trong schema·định kiểu mạnh
Caching Tận dụng tự nhiên HTTP cache (GET+URL) Cần thiết kế riêng cache tầng ứng dụng
Quản lý phiên bản Xu hướng rẽ nhánh /v1·/v2 Tiến hóa bằng thêm·ngừng dùng (deprecate) trường
Đường cong học tập·hệ sinh thái Thấp·trưởng thành Tương đối cao

Đặc biệt, khác biệt về caching ảnh hưởng lớn đến thiết kế thực tế. Trong REST, URL định danh duy nhất tài nguyên và GET là lũy đẳng, nên CDN, trình duyệt, proxy có thể dùng nguyên HTTP cache chuẩn. Ngược lại, GraphQL thường trao đổi nhiều truy vấn khác nhau qua một endpoint POST duy nhất nên cache dựa trên URL bị vô hiệu, cần chiến lược riêng như cache chuẩn hóa của thư viện client (ví dụ: theo id của đối tượng) hoặc tạo khóa cache bằng truy vấn lưu sẵn (persisted query). Đây là lý do tiêu biểu khiến quan niệm "GraphQL luôn nhanh" không đúng.

Trong thực tế, thay vì hai lựa chọn loại trừ, việc dùng kết hợp là phổ biến. Ví dụ, API công khai bên ngoài, tải tệp lên, CRUD đơn giản để ở REST, còn chỉ các màn hình phức hợp cần kết hợp nhiều nguồn hoặc tầng BFF di động mới dùng GraphQL. Các tình huống tiêu biểu tại Hàn Quốc và nước ngoài là GitHub cung cấp song song API GraphQL (v4) cùng với REST, và Shopify, Netflix... áp dụng GraphQL cho tầng kết hợp dịch vụ nội bộ.

5. Chuyên sâu — Mối đe dọa bảo mật, hiệu năng và Federation

Sự linh hoạt của GraphQL cũng chính là bề mặt tấn công. Client có thể tự do xây dựng cấu trúc truy vấn nên có thể xảy ra tấn công độ sâu và độ phức tạp truy vấn làm cạn kiệt tài nguyên máy chủ bằng truy vấn lồng sâu (ví dụ: friends { friends { friends ... } }). Để ngăn chặn, trong thực tế kết hợp ① giới hạn độ sâu (depth) truy vấn tối đa, ② phân tích chi phí truy vấn (query cost analysis) cộng dồn chi phí theo trường và từ chối nếu vượt ngưỡng, ③ danh sách cho phép (allowlist) truy vấn lưu sẵn (persisted query) chỉ cho phép các truy vấn đã đăng ký trước, ④ giới hạn tốc độ (rate limiting). Ngoài ra, introspection mang lại tiện lợi cho phát triển nhưng được khuyến nghị vô hiệu hóa trong môi trường vận hành để giảm lộ schema nội bộ.

Phân quyền (authorization) cũng cần cách tiếp cận khác với REST. REST có thể gắn quyền theo đơn vị endpoint, nhưng trong GraphQL một truy vấn đi qua nhiều kiểu và trường nên đòi hỏi phân quyền mức trường (field-level). Tức là phải kiểm tra quyền của bên gọi cho từng trường trong resolver hoặc chỉ thị schema (directive), và nếu bỏ sót sẽ phát sinh lỗi người dùng không có quyền truy cập dữ liệu nhạy cảm thông qua trường lồng nhau.

Trong tổ chức quy mô lớn, vấn đề schema phình to được giải quyết bằng GraphQL Federation (Federation). Nhiều nhóm vận hành độc lập các đồ thị con (subgraph) mà mỗi nhóm sở hữu, và gateway tổng hợp chúng thành một đồ thị tích hợp (supergraph). Điều này căn chỉnh cấu trúc tổ chức microservice với quyền sở hữu schema, giúp tránh tình trạng một nhóm quản lý schema khổng lồ duy nhất như một nút thắt. Tuy nhiên, federation đưa vào chi phí kết hợp ở gateway và gánh nặng truy vết phân tán (observability) mới, nên chỉ hợp lý khi quy mô tổ chức và ranh giới nhóm đủ lớn.

6. Lưu ý và hàm ý

Thứ nhất, quyết định áp dụng phải xuất phát từ phân tích đánh đổi. GraphQL có lợi lớn trong tình huống nhiều client yêu cầu dữ liệu khác nhau và khứ hồi mạng là nút thắt (di động, dashboard phức hợp, kết hợp nhiều backend). Ngược lại, với CRUD đơn giản, API công khai cần HTTP caching mạnh, dịch vụ chủ yếu truyền tệp theo luồng thì REST đơn giản và an toàn hơn. Phải lựa chọn dựa trên sự đa dạng của client và độ phức tạp kết hợp dữ liệu chứ không phải theo "trào lưu".

Thứ hai, hiệu năng không miễn phí mà là kết quả của thiết kế. Nếu không thiết kế đồng thời batch DataLoader cho vấn đề N+1, caching kết quả truy vấn, bảo đảm khóa cache qua truy vấn lưu sẵn, GraphQL có thể còn chậm và nặng hơn REST. Phải bảo đảm khả năng quan sát (thời gian thực thi resolver theo từng yêu cầu, giám sát độ phức tạp truy vấn) ngay từ đầu.

Thứ ba, bảo mật phải được thiết kế lại theo đơn vị trường. Phải lấy giới hạn độ sâu và độ phức tạp truy vấn, danh sách cho phép truy vấn lưu sẵn, kiểm soát introspection trong môi trường vận hành, phân quyền mức trường làm mặc định, và nên cưỡng chế chúng ở tầng chung như gateway, chỉ thị schema thay vì chỉ giao cho mã ứng dụng.

Thứ tư, quản trị và chiến lược tiến hóa schema quyết định thành bại. GraphQL hướng đến tiến hóa dần bằng thêm trường và đánh dấu @deprecated thay vì rẽ nhánh phiên bản. Do đó cần đưa kiểm tra tương thích ngược của thay đổi schema (schema check) vào CI, và khi tổ chức lớn lên thì phân tán quyền sở hữu schema bằng federation — tức là cần hệ thống vận hành quản lý API như một sản phẩm. Theo góc nhìn Kỹ sư chuyên nghiệp Quản lý Thông tin, hợp lý khi xem GraphQL không phải là một công nghệ đơn lẻ mà là bài toán quyết định kiến trúc đan xen hợp đồng API, cấu trúc tổ chức, hiệu năng, bảo mật.

Tài liệu tham khảo


Tóm tắt một câu: GraphQL là đặc tả API cho phép client chỉ chọn những trường cần thiết trong schema định kiểu mạnh và truy vấn qua một endpoint duy nhất, giải quyết lấy thừa/thiếu và bùng nổ endpoint, nhưng là công nghệ đánh đổi đòi hỏi phải gánh gánh nặng caching, bảo mật và hiệu năng N+1 bằng thiết kế.