Mục lục
- Mục Tiêu Bài Học
- REST — Resource-Oriented Architectural Style
- RPC — Remote Procedure Call
- GraphQL — Query Language Cho API
- Bảng Trade-Off Tổng Quan
- Khi Nào Chọn Mỗi Cái — Decision Matrix
- Hybrid Pattern Trong Production Thực Tế
- Tại Sao Series Chọn REST + Capstone Có Cả gRPC Và GraphQL
- Tổng Kết
- Bài Tập Củng Cố
- Bài Tiếp Theo
Mục Tiêu Bài Học
Sau bài học, bạn sẽ:
- Hiểu ba architectural style chính cho web API: REST, RPC, GraphQL.
- Nắm khác biệt cốt lõi giữa resource-oriented (REST), function-call (RPC) và query (GraphQL).
- Biết các flavor RPC khác nhau: gRPC, JSON-RPC, SOAP — mỗi cái mạnh ở context nào.
- Hiểu GraphQL flexible query, vấn đề N+1, và pattern DataLoader.
- Đọc được bảng trade-off đa chiều giữa ba style.
- Có decision matrix để biết khi nào chọn cái nào trong thực tế.
- Biết vì sao Shop API chọn REST (cộng với capstone B313 GraphQL và B314 gRPC ở cuối series để bạn so sánh hands-on cùng domain).
REST — Resource-Oriented Architectural Style
Recap từ B1: REST do Roy Fielding công bố năm 2000 trong luận án tiến sĩ, định nghĩa qua sáu constraint (client-server, stateless, cacheable, layered system, uniform interface, code-on-demand optional). Triết lý cốt lõi: mọi thứ trên server là một resource, được định danh bằng URL, thao tác qua HTTP verb chuẩn (GET đọc, POST tạo, PUT thay thế, PATCH cập nhật một phần, DELETE xóa). HTTP là lớp transport tự nhiên, không phải lớp gắn thêm.
GET /api/v1/products/43 HTTP/1.1
Host: api.shop.example
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Strength:
- Đơn giản và HTTP-native — không cần học giao thức riêng, dev backend nào cũng đã biết HTTP cơ bản.
- Cacheable qua HTTP cache layer (browser, CDN như Cloudflare/Fastly, reverse proxy như Varnish) thông qua
Cache-Control,ETag,Last-Modified. Không phải làm thêm gì, hạ tầng web đã hỗ trợ sẵn. - Browser-friendly — phát từ trình duyệt qua
fetch()hoặcXMLHttpRequestkhông cần thư viện thứ ba; mọi proxy, firewall, IDS đều hiểu HTTP/1.1 chuẩn. - Tooling mature — curl, Postman, Insomnia, HTTPie, Bruno, hệ sinh thái 20+ năm.
- Document tốt qua OpenAPI 3.1 và Swagger UI (chi tiết B8).
Weakness:
- Over-fetching — endpoint
GET /api/v1/products/43trả full object với 30 field, nhưng client mobile chỉ cầnnamevàprice. Phí băng thông. - Under-fetching — màn hình chi tiết sản phẩm cần
product+category+reviews+related_products: client phải gọi bốn round trip nếu mỗi resource có endpoint riêng (anti-pattern chatty API). - Versioning phức tạp khi schema thay đổi — phải dùng URL versioning
/api/v1→/api/v2hoặc vendor MIME, không tự động. - Contract lỏng — REST không bắt buộc schema; OpenAPI là tùy chọn, dễ drift giữa spec và code thật.
Best for: public API cho third-party developer, mobile/web client phổ thông, CRUD-heavy domain (e-commerce, blog, social), tích hợp B2B với contract document hóa qua Swagger.
RPC — Remote Procedure Call
RPC (Remote Procedure Call) có triết lý khác hẳn REST: client gọi function/method trên server như gọi local — đầy đủ tham số đầu vào, nhận giá trị trả về. Không có khái niệm resource hay URL có ý nghĩa. URL chỉ là entry point, payload chứa tên method và arguments.
Ba flavor RPC phổ biến nhất:
gRPC (Google, công bố 2015, open source qua CNCF) — flavor RPC hiện đại được dùng rộng rãi nhất. Đặc trưng:
- Transport là HTTP/2 binary, hỗ trợ multiplexing nhiều stream cùng connection, header compression (HPACK).
- Format payload là Protocol Buffers (protobuf) — schema-first binary, nhỏ gấp 3-5 lần JSON cho cùng dữ liệu.
- Contract bắt buộc qua file
.proto, dùngprotochoặc codegen tool sinh ra client/server stub cho 11+ ngôn ngữ (Rust, Go, Java, Python, C++, ...). - Hỗ trợ streaming bidirectional native — server stream, client stream, hoặc cả hai.
syntax = "proto3";
package shop.v1;
service ProductService {
rpc GetProduct(GetProductRequest) returns (Product);
rpc ListProducts(ListProductsRequest) returns (stream Product);
}
message GetProductRequest {
int64 id = 1;
}
message Product {
int64 id = 1;
string slug = 2;
string name = 3;
string price = 4; // Decimal serialize as string
}
JSON-RPC (spec 2.0 từ 2010) — flavor RPC text-based, payload là JSON với field bắt buộc jsonrpc, method, params, id. Đơn giản, dễ debug, không cần codegen, chạy qua HTTP hoặc WebSocket. Dùng nhiều trong blockchain (Ethereum JSON-RPC API), JSON-RPC over WebSocket cho real-time bidding.
SOAP (Simple Object Access Protocol, đỉnh cao 2003-2010) — flavor RPC enterprise legacy, payload là XML envelope với chuẩn WS-* (WS-Security, WS-Addressing, WS-AtomicTransaction). Vẫn còn ở banking, EDI, healthcare claims, government. Mới xây thì không nên chọn SOAP; chỉ hiểu để integrate với legacy.
Strength:
- Contract-first strict — file
.proto(gRPC) hoặc WSDL (SOAP) là single source of truth; client/server không bao giờ lệch nhau ở field name hay type. - Type-safe codegen — sinh code Rust struct, Go struct, Java class tự động; compiler bắt lỗi field thiếu hoặc sai type.
- Binary performance (gRPC) — payload nhỏ, parse nhanh, serialize nhanh; HTTP/2 multiplex giảm latency tổng thể trên service mesh.
- Streaming native — gRPC stream bidirectional là feature first-class, không phải workaround như SSE hay WebSocket.
Weakness:
- Browser-unfriendly — gRPC binary HTTP/2 không gọi trực tiếp từ browser được; phải dùng gRPC-Web proxy (Envoy) transcode sang HTTP/1.1. Tăng complexity infrastructure.
- Khó qua proxy/firewall — một số corporate proxy không support HTTP/2 hoặc method CONNECT cho streaming, gây lỗi khó debug.
- Debug khó hơn JSON — payload binary, không đọc thủ công được, phải dùng tool như
grpcurlhoặc BloomRPC. - Cache HTTP layer không áp dụng — request POST với binary body, CDN/Varnish/browser cache không hiểu.
Best for: internal service-to-service trong microservice cluster (East-West traffic), streaming bidirectional (chat, video, IoT telemetry), domain performance-critical (high-frequency trading, ad serving), polyglot team cần codegen nhiều ngôn ngữ. Rust ecosystem dùng crate tonic — gRPC framework chuẩn de facto.
GraphQL — Query Language Cho API
GraphQL do Facebook phát triển nội bộ từ 2012, công bố 2015, chuyển sang foundation độc lập GraphQL Foundation năm 2018. Triết lý: client mô tả chính xác fields cần lấy bằng một query language; server có resolver match từng field trong query, trả về JSON đúng shape mà client yêu cầu.
Khác REST ở mọi mặt:
- Single endpoint
/graphqlcho mọi operation — không có URL per resource. - Mọi request là POST với body chứa query string. Không có
GET /products, không cóPOST /products. - Three operation type:
query(đọc),mutation(ghi),subscription(real-time qua WebSocket). - Schema strongly typed, hai cách tiếp cận: schema-first (viết SDL trước) hoặc code-first (define qua code, sinh schema).
query GetProductDetail($slug: String!) {
product(slug: $slug) {
id
name
price
category {
name
slug
}
reviews(limit: 5) {
rating
comment
user { name }
}
}
}
Query trên gom dữ liệu từ ba "resource" (product, category, reviews) trong một round trip; client chỉ nhận đúng field mình liệt kê, không thừa không thiếu.
Strength:
- Client-driven — không over-fetching, không under-fetching. Mobile lấy 5 field, web lấy 30 field, cùng một endpoint cùng schema.
- Single endpoint — đơn giản hóa routing, không phải document hàng chục URL.
- Type-safe schema — codegen client (TypeScript, Swift, Kotlin) từ schema; IDE autocomplete query.
- Introspection built-in — query
__schematrả full schema; tool như GraphiQL hay Apollo Studio auto-render IDE từ introspection.
Weakness:
- Cache HTTP không áp dụng — mọi request POST cùng URL với body khác → CDN/browser cache không hiểu. Phải làm cache layer riêng (Apollo Client cache, Relay store, Redis).
- N+1 problem — query lấy 100 product mỗi product có category: naive resolver query category lần lượt cho từng product → 1 + 100 = 101 DB query. Pattern fix là DataLoader: batch nhiều ID category lại trong 1 query DB, dùng cache trong vòng đời 1 request.
- Authorization granular phức tạp — phải check quyền per-field, không phải per-endpoint như REST.
- Learning curve cao — schema design, resolver pattern, DataLoader, batching, pagination cursor (Relay spec) đều cần học riêng.
Best for: mobile app cần optimize data over network (giảm số request, giảm payload), nhiều client cùng backend (web + iOS + Android) với UI khác nhau, BFF (backend-for-frontend) aggregation từ nhiều microservice. Rust ecosystem dùng crate async-graphql — framework GraphQL server hiện đại nhất.
Bảng Trade-Off Tổng Quan
Ba style không phải cạnh tranh trực tiếp; mỗi cái mạnh ở context khác nhau. Bảng dưới so sánh đa chiều để bạn nhanh chóng map yêu cầu thực tế sang style phù hợp:
Dimension | REST | gRPC | GraphQL
-------------------|-----------------------|------------------------|------------------------
Transport | HTTP/1.1 hoặc HTTP/2 | HTTP/2 (binary) | HTTP (POST)
Format payload | JSON (mostly) | Protobuf binary | JSON
Contract | OpenAPI (optional) | .proto (required) | GraphQL Schema (req)
Versioning | URL hoặc header | Proto backward-compat | Field deprecation
HTTP cache native | Có (Cache-Control) | Không (binary POST) | Không (single endpt)
Tool support | Universal (curl, ...) | Cần codegen + grpcurl | Specific (GraphiQL)
Browser-friendly | Có | Không (cần gRPC-Web) | Có (POST JSON)
Real-time | SSE / WebSocket | Streaming native | Subscription qua WS
Learning curve | Thấp | Trung bình | Cao
Payload size | Trung bình | Nhỏ nhất (~30% JSON) | Lớn (text JSON)
Use case mạnh nhất | Public API, CRUD | Internal service mesh | Multi-client BFF
Vài quan sát quan trọng từ bảng:
- HTTP cache native chỉ REST hỗ trợ — đây là lợi thế chiến lược cho public API có read-heavy traffic.
- Browser-friendly: REST và GraphQL gọi trực tiếp từ browser được; gRPC bắt buộc proxy gRPC-Web (Envoy thường được dùng) cho kịch bản browser → server gRPC.
- Payload size: gRPC nhỏ nhất vì binary protobuf; JSON ở REST/GraphQL nặng hơn nhưng dễ debug bằng mắt.
- Learning curve: GraphQL cao nhất vì schema design + DataLoader + authorization per-field; REST thấp nhất vì chỉ là HTTP cơ bản.
Khi Nào Chọn Mỗi Cái — Decision Matrix
Chọn REST khi:
- Public API mở cho third-party developer, mobile, web — ecosystem tool sẵn (Postman, OpenAPI client SDK).
- Domain CRUD-heavy với resource model rõ ràng (e-commerce, blog, social, booking).
- Cần HTTP caching native qua CDN (Cloudflare, Fastly) hoặc browser cache cho read-heavy endpoint.
- Team junior hoặc cần onboarding nhanh — REST đơn giản nhất trong ba style.
- Cần document tốt cho external consumer qua Swagger UI / Redoc.
Chọn gRPC khi:
- Internal service-to-service trong microservice cluster (East-West traffic giữa các service nội bộ).
- Cần performance cao — binary nhỏ, HTTP/2 multiplex giảm latency trên 1 connection nhiều request.
- Streaming bidirectional là first-class requirement (chat real-time, video pipeline, IoT telemetry, ad bidding).
- Polyglot team — codegen 11+ ngôn ngữ từ cùng một file
.protobảo đảm consistency. - Cần type-safe contract strict, không cho phép field drift giữa client và server.
Chọn GraphQL khi:
- Mobile app cần optimize data over network (low bandwidth, pay-per-MB) — gọi 1 query trả đúng field cần.
- UI phức tạp với data shape thay đổi nhanh; ba client (web/iOS/Android) cùng backend nhưng nhu cầu field khác nhau.
- Aggregation từ nhiều microservice — GraphQL ở vai trò BFF (backend-for-frontend) gọi các service nội bộ rồi gom lại.
- Cần introspection cho tooling tự động (IDE autocomplete query, schema diff giữa versions).
Hybrid Pattern Trong Production Thực Tế
Công ty lớn hiếm khi chọn "tất cả REST" hay "tất cả gRPC". Câu hỏi thực tế là: với từng domain và từng traffic pattern, style nào hợp nhất? Phần lớn production stack lai cả ba.
Pattern phổ biến trong industry:
- REST cho external/public API — bề mặt mở cho third-party, mobile, web client. Compatible mọi tool, document Swagger, dễ marketing API.
- gRPC cho internal service mesh — service ↔ service nội bộ, ẩn sau API Gateway. Tận dụng HTTP/2 multiplex, binary nhỏ, streaming native. Hạ tầng service mesh như Linkerd hoặc Istio hỗ trợ gRPC observability, retry, circuit breaker first-class.
- GraphQL cho BFF layer mobile — đặt giữa mobile app và backend microservice. Mobile gọi GraphQL query lấy đúng field cần; BFF gọi REST/gRPC tới các service nội bộ rồi gom kết quả.
Ví dụ thực tế:
- Netflix: REST cho public API (developer portal), gRPC nội bộ cho service ↔ service (~1000 microservice), GraphQL Federation cho aggregation layer phục vụ Netflix Studio và partner.
- Shopify: REST Admin API cho merchant, GraphQL Storefront API cho mobile và headless commerce client, gRPC nội bộ cho hot path checkout.
- Twitter/X: REST cho public v2 API, gRPC nội bộ cho timeline service, GraphQL cho mobile app TweetDeck/web client.
Bài học: không phải "all-or-nothing". Architect chọn theo từng domain, từng client type, từng SLA — và đầu tư learning curve cho team theo thứ tự ưu tiên thực tế.
Tại Sao Series Chọn REST + Capstone Có Cả gRPC Và GraphQL
Series chính dạy bạn xây Shop API bằng axum theo REST. Lý do chọn REST làm primary protocol:
- REST là baseline mọi backend Rust dev nên biết — phỏng vấn, dự án thật, OSS contribution đều dính.
- HTTP knowledge (method, status code, header, content negotiation, JSON) đã lock ở B1-B6 là foundation chung cho cả gRPC (HTTP/2) và GraphQL (HTTP POST) — không hề wasted nếu sau này bạn chuyển sang style khác.
- Shop API là e-commerce CRUD-heavy domain, public API cho mobile/web, cần HTTP cache cho catalog endpoint — đúng sweet spot của REST.
Để bạn không bị giới hạn ở REST khi vào thực tế, series dành hai capstone ở cuối (Group 32):
- B313 — Capstone 3: GraphQL Server với
async-graphql. Re-implement subset Shop domain (Product, Category, Cart, Order) qua GraphQL. Bạn sẽ tự tay viết schema, resolver, DataLoader cho N+1, mutation, subscription cho order status update. So sánh side-by-side với REST đã viết suốt 312 bài. - B314 — Capstone 4: gRPC Service với
tonic. Re-implement subset Shop qua gRPC: viết file.protocho ProductService/CartService/OrderService, codegen Rust stub, implement service, test vớigrpcurl, streaming list product. So sánh performance và contract style với REST.
Cùng một domain (Shop), khác protocol — đây là cách hiệu quả nhất để cảm nhận trade-off thật của từng style, vì context business đã giữ cố định.
# Cargo.toml workspace dependencies preview
[workspace.dependencies]
# REST main (Group 1-31)
axum = "0.8"
tower-http = "0.6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# B313 Capstone 3: GraphQL
async-graphql = { version = "7", features = ["chrono", "uuid"] }
async-graphql-axum = "7"
# B314 Capstone 4: gRPC
tonic = "0.13"
prost = "0.14"
tonic-build = "0.13" # build-dependency cho .proto codegen
Quyết định production trong nghề: REST first cho public API; gRPC khi chuyển sang microservice nội bộ hoặc cần streaming/perf cao; GraphQL khi mobile client là driver chính hoặc cần BFF aggregation đa service. Không có cái nào sai — chỉ có cái nào phù hợp context cụ thể của bạn.
Tổng Kết
- REST: resource-oriented, HTTP-native, cacheable qua CDN, browser-friendly, tool universal — sweet spot cho public API và CRUD-heavy domain.
- RPC: function-call style; ba flavor chính là gRPC (binary HTTP/2, streaming native, contract
.protobắt buộc), JSON-RPC (text-based đơn giản, dùng nhiều ở blockchain), SOAP (XML enterprise legacy). gRPC mạnh nhất cho internal service-to-service. - GraphQL: client-driven query, single endpoint
/graphql, schema strong-typed, type-safe codegen client; trade-off lớn là cache HTTP không áp dụng, N+1 problem cần DataLoader, authorization granular phức tạp. - HTTP cache native: chỉ REST hỗ trợ; gRPC khó cache (binary POST), GraphQL khó cache HTTP (single endpoint POST) — cần cache layer riêng (Apollo Client, Relay store, Redis).
- Hybrid pattern trong production: REST cho external public API, gRPC cho internal service mesh (Linkerd/Istio), GraphQL cho BFF layer mobile. Netflix, Shopify, Twitter đều dùng cả ba cho domain khác nhau.
- Series Shop API chọn REST primary với axum vì baseline mọi backend dev nên biết; HTTP foundation (B1-B6) tái sử dụng được cho cả ba style.
- Capstone cuối series: B313 GraphQL với
async-graphql+ B314 gRPC vớitonicre-implement subset Shop domain — bạn được so sánh hands-on cùng business context, khác protocol.
Bài Tập Củng Cố
Tự trả lời, đáp án ở cuối:
- REST và RPC khác nhau ở triết lý gì? Cho ví dụ một endpoint REST và một method RPC tương ứng để minh họa resource vs function call.
- gRPC dùng format gì cho payload? Có ưu điểm gì so với JSON về kích thước và performance? Tại sao gRPC khó gọi trực tiếp từ browser?
- GraphQL giải quyết vấn đề over-fetching như thế nào? Trade-off lớn nhất khi triển khai GraphQL trong production là gì? N+1 problem là gì và pattern nào fix nó?
- Team mobile app phàn nàn rằng REST API trả quá nhiều field không cần, tốn băng thông. Có hai giải pháp: (a) thêm query param
?fields=name,pricefilter field trên REST endpoint sẵn có, (b) chuyển sang GraphQL. Khi nào dùng giải pháp nào? Yếu tố quyết định? - Shop API trong series này chọn REST. Series có dạy gRPC và GraphQL không? Nếu có thì ở bài nào? Triết lý sư phạm của hai capstone đó là gì?
Đáp án
- REST là resource-oriented — coi mọi thứ trên server là resource có URL định danh, thao tác qua HTTP verb chuẩn (GET/POST/PUT/PATCH/DELETE). Action không map CRUD vẫn dùng POST verb-noun (vd
POST /api/v1/orders/:id/cancel). RPC là function call — coi server là tập hợp method, client gọi tên method với tham số như gọi local. Ví dụ minh họa: RESTGET /api/v1/products/43(lấy resource Product id=43) tương ứng RPCProductService.GetProduct(GetProductRequest { id: 43 })(gọi function GetProduct với param). URL trong REST có ý nghĩa (resource path), URL trong RPC chỉ là entry point (vd/grpc), method và param nằm trong payload. - gRPC dùng Protocol Buffers (protobuf) — binary format schema-first. Ưu điểm so với JSON: (1) Kích thước nhỏ hơn ~30-70% cho cùng dữ liệu vì không có tên field trong payload (chỉ có tag number), không có whitespace, không quote field name. (2) Parse/serialize nhanh hơn ~5-10 lần vì binary đã tagged, không phải tokenize text. (3) Compile-time type-safe qua codegen Rust struct/Go struct/Java class. Lý do gRPC khó gọi từ browser: dùng HTTP/2 binary với feature như trailer headers và streaming bidirectional mà
fetch()API của browser không hỗ trợ đầy đủ. Fix là dùng gRPC-Web spec — chạy proxy Envoy ở edge để transcode gRPC ↔ HTTP/1.1 cho browser. Tăng complexity infrastructure, nên thường giữ gRPC trong nội bộ backend, expose REST/GraphQL ra browser. - GraphQL fix over-fetching bằng cách cho client tự liệt kê field cần trong query: query chỉ ghi
{ product(id: 43) { name price } }thì server chỉ trả hai field đó, không trả 30 field như REST endpoint. Trade-off lớn nhất trong production: (1) Cache HTTP layer không áp dụng — mọi request POST cùng URL, body khác → CDN/browser cache không hiểu. Phải làm cache layer riêng (Apollo Client, Relay, Redis). (2) Authorization granular phức tạp — phải check quyền per-field thay vì per-endpoint, dễ leak field nhạy cảm. (3) Query complexity attack — client gửi query lồng sâu nhiều cấp gây quá tải DB, phải có depth limit và complexity scoring. N+1 problem: query lấy danh sách N item, mỗi item resolve một sub-field cần query DB → naive resolver chạy 1 + N query DB (1 cho list, N cho mỗi item). Pattern fix là DataLoader: gom batch nhiều ID lại trong một vòng đời request, query DB một lần bằngSELECT ... WHERE id IN (...), cache kết quả per-request. Crate Rustasync-graphqlcódataloadermodule built-in. - Giải pháp (a) field filtering trên REST (vd
GET /api/v1/products/43?fields=name,price) — phù hợp khi: nhu cầu filter field đơn giản, chỉ áp dụng cho vài endpoint hot, team đã có REST stack ổn định, không muốn migrate. Đây là pattern sparse fieldsets theo JSON:API spec. Nhược điểm: vẫn không giải quyết under-fetching (cần product + category + reviews vẫn phải 3 request), không type-safe, dễ misuse. Giải pháp (b) chuyển sang GraphQL — phù hợp khi: nhu cầu field thay đổi nhanh và đa dạng giữa nhiều client (web/iOS/Android), có aggregation từ nhiều resource (under-fetching nghiêm trọng), team chấp nhận đầu tư learning curve và infrastructure (Apollo Client, DataLoader, monitoring). Yếu tố quyết định chính: scale của vấn đề. Nếu chỉ 2-3 endpoint hot mobile cần filter, REST + query param đủ; nếu cả app mobile có 30+ màn hình mỗi màn cần data shape khác nhau, GraphQL win về long-term maintainability dù chi phí setup ban đầu cao hơn. - Có. Series dạy ở hai capstone cuối Group 32: B313 — Capstone 3: GraphQL Server với
async-graphqlvà B314 — Capstone 4: gRPC Service vớitonic. Triết lý sư phạm: cả hai capstone re-implement subset Shop domain (Product, Category, Cart, Order) qua protocol mới. Giữ cố định business domain (đã quen sau 312 bài), thay đổi protocol — bạn cảm nhận trade-off thật của từng style trong cùng context business. B313 tập trung: schema design, resolver, DataLoader fix N+1, mutation, subscription qua WebSocket. B314 tập trung: file.proto, codegen Rust stub, implement service trait, test vớigrpcurl, server streaming. Sau hai capstone bạn có quyết định informed: tại sao Shop API public dùng REST, nhưng nếu Shop có service nội bộ inventory hot path nên cân nhắc gRPC, hoặc nếu mobile app Shop có shape data phức tạp nên có GraphQL BFF layer.
Bài Tiếp Theo
Bài 8: OpenAPI & Swagger — API Documentation — đi sâu vào OpenAPI 3.1 spec, Swagger UI, crate utoipa cho axum (derive macro auto-generate spec từ handler), API-first vs code-first workflow, expose /api-doc/openapi.json và /swagger-ui cho Shop API ở dev/staging.
