Skip to content

📡 SYNCHRONOUS COMMUNICATION

REST vs gRPC vs GraphQL: Cuộc chiến của các Giao thức


Mục tiêu Học tập

Sau khi hoàn thành module này, bạn sẽ có khả năng:

  • Phân tích trade-offs giữa REST, gRPC, và GraphQL
  • Hiểu tại sao gRPC tối ưu cho Service-to-Service communication
  • Giải quyết vấn đề Over-fetchingUnder-fetching
  • Áp dụng Contract-first Design với Protocol Buffers

1. The Fallacy of "One Size Fits All"

NOTE

🎓 Giáo sư Tom: Một trong những sai lầm lớn nhất trong API design là dùng cùng một protocol cho mọi thứ. REST tuyệt vời cho public APIs, nhưng là "overkill" cho internal service calls. gRPC nhanh hơn 10x trong microservices, nhưng browsers không support native. Hiểu context là key.

┌─────────────────────────────────────────────────────────────────────────┐
│                    COMMUNICATION ARCHITECTURE                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │                        EXTERNAL CLIENTS                          │   │
│   │                                                                  │   │
│   │   🌐 Browser    📱 Mobile App    🤖 Third-party API Consumer    │   │
│   │                                                                  │   │
│   └──────────────────────────┬──────────────────────────────────────┘   │
│                              │                                           │
│                    ┌─────────▼─────────┐                                │
│                    │   API GATEWAY     │                                │
│                    │   (REST/GraphQL)  │  ◄─── Human-readable           │
│                    │                   │       Browser compatible       │
│                    └─────────┬─────────┘                                │
│                              │                                           │
│   ══════════════════════════╪════════════════════════════════════════   │
│                              │ INTERNAL NETWORK                          │
│   ══════════════════════════╪════════════════════════════════════════   │
│                              │                                           │
│   ┌──────────────────────────▼──────────────────────────────────────┐   │
│   │                    MICROSERVICES                                 │   │
│   │                                                                  │   │
│   │   ┌─────────┐    gRPC    ┌─────────┐    gRPC    ┌─────────┐    │   │
│   │   │ Service │◄──────────►│ Service │◄──────────►│ Service │    │   │
│   │   │    A    │            │    B    │            │    C    │    │   │
│   │   └─────────┘            └─────────┘            └─────────┘    │   │
│   │                                                                  │   │
│   │   ◄─── Binary protocol, code generation, type safety ─────────►│   │
│   │                                                                  │   │
│   └──────────────────────────────────────────────────────────────────┘   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

2. REST: The Workhorse

2.1 REST Principles

┌─────────────────────────────────────────────────────────────────────────┐
│                       REST FUNDAMENTALS                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   CORE CONSTRAINTS:                                                      │
│   ═════════════════                                                      │
│                                                                          │
│   1. STATELESS                                                           │
│      Each request contains ALL information needed                       │
│      Server doesn't store client context between requests               │
│                                                                          │
│   2. UNIFORM INTERFACE                                                   │
│      GET     → Read resource                                            │
│      POST    → Create resource                                          │
│      PUT     → Replace resource                                         │
│      PATCH   → Partial update                                           │
│      DELETE  → Remove resource                                          │
│                                                                          │
│   3. RESOURCE-BASED                                                      │
│      URLs represent resources (nouns), not actions (verbs)              │
│                                                                          │
│      ✅ GET /users/123                                                  │
│      ❌ GET /getUser?id=123                                             │
│      ❌ POST /createUser                                                │
│                                                                          │
│   4. CACHEABLE                                                           │
│      Response should be cacheable when appropriate                      │
│      Cache-Control, ETag, Last-Modified headers                         │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

2.2 REST Problems: Over-fetching & Under-fetching

┌─────────────────────────────────────────────────────────────────────────┐
│                    OVER-FETCHING PROBLEM                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Mobile App needs: User's name and avatar (for header)                 │
│                                                                          │
│   Request: GET /api/users/123                                           │
│                                                                          │
│   Response (500 bytes):                                                  │
│   {                                                                      │
│     "id": 123,                                                          │
│     "name": "Nguyen Van A",         ◄─── NEEDED                         │
│     "avatar": "https://...",        ◄─── NEEDED                         │
│     "email": "a@example.com",       ◄─── WASTED                         │
│     "phone": "0901234567",          ◄─── WASTED                         │
│     "address": "123 Nguyen Hue",    ◄─── WASTED                         │
│     "bio": "Full-stack dev...",     ◄─── WASTED                         │
│     "createdAt": "2023-01-15",      ◄─── WASTED                         │
│     "settings": { ... },            ◄─── WASTED                         │
│     "subscriptions": [ ... ]        ◄─── WASTED                         │
│   }                                                                      │
│                                                                          │
│   Mobile only needed 50 bytes, got 500 bytes → 10x waste                │
│   × 10,000 users = 5MB wasted bandwidth                                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                   UNDER-FETCHING PROBLEM                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Frontend needs: User + Recent Orders + Favorite Products              │
│                                                                          │
│   With REST:                                                             │
│   ══════════                                                             │
│   Request 1: GET /api/users/123                                         │
│   Request 2: GET /api/users/123/orders?limit=5                          │
│   Request 3: GET /api/users/123/favorites                               │
│                                                                          │
│   = 3 round trips = 3 × latency                                         │
│                                                                          │
│   If each request = 100ms latency:                                       │
│   Total = 300ms (serial) or 100ms (parallel, but 3x connections)        │
│                                                                          │
│   Mobile on 3G: Each request = 500ms                                    │
│   Total = 1.5 seconds just for API calls!                               │
│                                                                          │
│   Alternative: Create custom endpoint                                    │
│   GET /api/users/123/dashboard                                          │
│   → But now you have N endpoints for N screens                          │
│   → Backend becomes "frontend's slave"                                  │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

2.3 When to Use REST

ScenarioREST Suitability
Public API✅ Excellent - universally understood
Third-party integration✅ Everyone knows REST
Browser direct calls✅ Native support
Simple CRUD✅ Maps perfectly
Internal microservices⚠️ Consider gRPC
Real-time data❌ Use WebSockets/gRPC streaming
High-throughput internal❌ REST overhead too high

3. gRPC: The Performance Monster

3.1 What is gRPC?

TIP

🔧 Kỹ sư Raizo: gRPC = gRPC Remote Procedure Call (recursive acronym!). Google dùng nội bộ từ 2001 với tên Stubby. Open-sourced năm 2015. Mọi Google service đều chạy trên gRPC - hàng tỷ RPC calls/second.

┌─────────────────────────────────────────────────────────────────────────┐
│                      gRPC ARCHITECTURE                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   ┌───────────────────────────────────────────────────────────────┐     │
│   │                     .proto FILE                                │     │
│   │                                                               │     │
│   │   syntax = "proto3";                                          │     │
│   │                                                               │     │
│   │   service UserService {                                       │     │
│   │     rpc GetUser(UserRequest) returns (UserResponse);          │     │
│   │     rpc ListUsers(ListRequest) returns (stream User);         │     │
│   │   }                                                           │     │
│   │                                                               │     │
│   │   message UserRequest {                                       │     │
│   │     int32 user_id = 1;                                        │     │
│   │   }                                                           │     │
│   │                                                               │     │
│   │   message UserResponse {                                      │     │
│   │     int32 id = 1;                                             │     │
│   │     string name = 2;                                          │     │
│   │     string email = 3;                                         │     │
│   │   }                                                           │     │
│   │                                                               │     │
│   └───────────────────────────────────────────────────────────────┘     │
│                              │                                           │
│              ┌───────────────┼───────────────┐                          │
│              │               │               │                           │
│              ▼               ▼               ▼                           │
│   ┌─────────────────┐ ┌─────────────┐ ┌─────────────┐                   │
│   │ Go Generated    │ │ Python Gen  │ │ Rust Gen    │                   │
│   │ Client + Server │ │ Client +    │ │ Client +    │                   │
│   │                 │ │ Server      │ │ Server      │                   │
│   └─────────────────┘ └─────────────┘ └─────────────┘                   │
│                                                                          │
│   Code generation = Type safety across ALL languages!                   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

3.2 Protobuf: Binary vs JSON

┌─────────────────────────────────────────────────────────────────────────┐
│                  PROTOBUF vs JSON ENCODING                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Same data: User { id: 123, name: "Nguyen", active: true }             │
│                                                                          │
│   JSON (67 bytes):                                                       │
│   ═══════════════                                                        │
│   {                                                                      │
│     "id": 123,                      ← 10 chars for field name           │
│     "name": "Nguyen",               ← Quotes, colons, commas            │
│     "active": true                  ← Boolean as 4-5 chars              │
│   }                                                                      │
│                                                                          │
│   Protobuf (11 bytes):                                                   │
│   ════════════════════                                                   │
│   08 7B 12 06 4E 67 75 79 65 6E 18 01                                   │
│   ├──┤ ├───────────────────────┤ ├────┤                                 │
│   id=123  name="Nguyen"          active=1                               │
│                                                                          │
│   Field names? Không encode! Chỉ dùng field number (1, 2, 3)            │
│   Integers? Varint encoding (nhỏ nhất có thể)                           │
│   Boolean? 1 byte                                                        │
│                                                                          │
│   Result: 67 bytes → 11 bytes = 83% smaller!                            │
│                                                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   PARSING SPEED:                                                         │
│   ══════════════                                                         │
│                                                                          │
│   JSON:                                                                  │
│   1. Tokenize string                                                     │
│   2. Parse field names (string compare)                                 │
│   3. Type conversion (string "123" → int 123)                           │
│   4. Build object                                                        │
│                                                                          │
│   Protobuf:                                                              │
│   1. Read field tag (1 byte: field number + type)                       │
│   2. Read value directly (already correct type)                         │
│   3. Done                                                                │
│                                                                          │
│   Benchmark: Protobuf parse 5-10x faster than JSON                      │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

3.3 gRPC Streaming Modes

┌─────────────────────────────────────────────────────────────────────────┐
│                    gRPC STREAMING MODES                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   1. UNARY (như REST)                                                    │
│   ════════════════════                                                   │
│   Client ─────── Request ──────► Server                                 │
│          ◄────── Response ───────                                       │
│                                                                          │
│   Use: Simple request/response                                          │
│                                                                          │
│   ─────────────────────────────────────────────────────────────────     │
│                                                                          │
│   2. SERVER STREAMING                                                    │
│   ════════════════════                                                   │
│   Client ─────── Request ──────► Server                                 │
│          ◄────── Response 1 ─────                                       │
│          ◄────── Response 2 ─────                                       │
│          ◄────── Response N ─────                                       │
│          ◄────── END ────────────                                       │
│                                                                          │
│   Use: Download large dataset, real-time updates                        │
│                                                                          │
│   ─────────────────────────────────────────────────────────────────     │
│                                                                          │
│   3. CLIENT STREAMING                                                    │
│   ════════════════════                                                   │
│   Client ─────── Request 1 ─────► Server                                │
│          ─────── Request 2 ─────►                                       │
│          ─────── Request N ─────►                                       │
│          ─────── END ───────────►                                       │
│          ◄────── Response ───────                                       │
│                                                                          │
│   Use: Upload large file, batch insert                                  │
│                                                                          │
│   ─────────────────────────────────────────────────────────────────     │
│                                                                          │
│   4. BIDIRECTIONAL STREAMING                                             │
│   ═══════════════════════════                                            │
│   Client ─────── Request 1 ─────► Server                                │
│          ◄────── Response 1 ─────                                       │
│          ─────── Request 2 ─────►                                       │
│          ◄────── Response 2 ─────                                       │
│          ◄────── Response 3 ─────                                       │
│          ─────── Request 3 ─────►                                       │
│                                                                          │
│   Use: Chat, real-time collaboration, gaming                            │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

3.4 gRPC Limitations

WARNING

gRPC không phải silver bullet:

  • Browser không support native (cần gRPC-Web proxy)
  • Không human-readable (binary format)
  • Khó debug với curl/Postman (cần grpcurl)
  • Learning curve cho team chưa quen

4. GraphQL: The Flexible One

4.1 How GraphQL Works

┌─────────────────────────────────────────────────────────────────────────┐
│                    GRAPHQL QUERY EXAMPLE                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   CLIENT QUERY:                                                          │
│   ═════════════                                                          │
│   query {                                                                │
│     user(id: 123) {                                                     │
│       name                   ← Only what I need                         │
│       avatar                 ← Only what I need                         │
│       recentOrders(limit: 5) {                                          │
│         id                                                              │
│         total                                                           │
│         status                                                          │
│       }                                                                 │
│       favoriteProducts(limit: 3) {                                      │
│         name                                                            │
│         price                                                           │
│       }                                                                 │
│     }                                                                   │
│   }                                                                      │
│                                                                          │
│   RESPONSE:                                                              │
│   ═════════                                                              │
│   {                                                                      │
│     "data": {                                                           │
│       "user": {                                                         │
│         "name": "Nguyen Van A",        ← Exactly what asked             │
│         "avatar": "https://...",                                        │
│         "recentOrders": [              ← Nested in ONE request          │
│           { "id": 1, "total": 100, "status": "delivered" },             │
│           { "id": 2, "total": 200, "status": "shipped" }                │
│         ],                                                              │
│         "favoriteProducts": [                                           │
│           { "name": "T-Shirt", "price": 25 }                            │
│         ]                                                               │
│       }                                                                 │
│     }                                                                   │
│   }                                                                      │
│                                                                          │
│   ✅ NO over-fetching (only requested fields)                           │
│   ✅ NO under-fetching (nested resources in 1 request)                  │
│   ✅ Frontend controls shape of response                                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

4.2 GraphQL Trade-offs

AdvantageDisadvantage
Flexible queriesComplexity in backend
No over/under-fetchingCaching is HARD
Single endpointN+1 query problem
Strong typingSecurity concerns (DoS via complex queries)
Self-documentingLearning curve

4.3 The Caching Problem

┌─────────────────────────────────────────────────────────────────────────┐
│                   GRAPHQL CACHING CHALLENGE                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   REST:                                                                  │
│   ═════                                                                  │
│   GET /users/123 → Cache key: "/users/123"                              │
│   GET /users/123 → HIT! Return cached response                          │
│                                                                          │
│   GraphQL:                                                               │
│   ════════                                                               │
│   POST /graphql                                                          │
│   { query: "{ user(id:123) { name } }" }                                │
│                                                                          │
│   POST /graphql                                                          │
│   { query: "{ user(id:123) { name email } }" }  ← Different query!      │
│                                                                          │
│   Cache key = query string? But queries are INFINITE                    │
│   → Need normalized cache (Apollo Client)                               │
│   → More complex client-side logic                                      │
│                                                                          │
│   CDN Caching:                                                           │
│   ════════════                                                           │
│   REST: CloudFlare/Fastly cache GET requests perfectly                  │
│   GraphQL: POST requests = no CDN caching by default                    │
│            → Workaround: Persisted queries (hash query → GET)          │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

5. Comparison Matrix

5.1 Feature Comparison

CriteriaRESTgRPCGraphQL
Payload FormatJSON (text)Protobuf (binary)JSON (text)
Payload SizeLargeSmall (60-80% less)Medium
Human Readable✅ Yes❌ No✅ Yes
Browser Native✅ Yes❌ No (needs proxy)✅ Yes
Code GenerationOptional✅ Built-inOptional
Streaming❌ Limited✅ Native support❌ Subscriptions only
Caching✅ Easy (HTTP)⚠️ Manual⚠️ Complex
Learning CurveLowMediumHigh

5.2 Performance Comparison

MetricRESTgRPCGraphQL
LatencyBaseline50-80% lowerSimilar to REST
ThroughputBaseline5-10x higherVaries
Serialization CPUHighLowHigh
Network BandwidthHighLowMedium

5.3 Use Case Matrix

┌─────────────────────────────────────────────────────────────────────────┐
│                    WHEN TO USE WHAT                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   USE REST WHEN:                                                         │
│   ══════════════                                                         │
│   • Public API (third-party developers)                                 │
│   • Simple CRUD operations                                              │
│   • Maximum compatibility needed                                        │
│   • Team new to API development                                         │
│                                                                          │
│   USE gRPC WHEN:                                                         │
│   ══════════════                                                         │
│   • Internal microservices communication                                │
│   • High-performance requirements                                       │
│   • Polyglot environment (Go ↔ Rust ↔ Python)                          │
│   • Streaming data needed                                               │
│   • Team comfortable with Protobuf                                      │
│                                                                          │
│   USE GRAPHQL WHEN:                                                      │
│   ════════════════                                                       │
│   • Mobile app with varying data needs                                  │
│   • Multiple frontend clients (web, iOS, Android)                       │
│   • Rapid frontend iteration needed                                     │
│   • Over-fetching is major problem                                      │
│   • Team has GraphQL expertise                                          │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

6. Raizo's Production Advice

TIP

🔧 Kỹ sư Raizo - HPN Architecture:

Penrift Cloud sử dụng hybrid approach:

  • External API: REST với OpenAPI spec (developer-friendly)
  • Internal Services: gRPC (Go services ↔ Rust services)
  • Admin Dashboard: GraphQL (rapid iteration needed)

Golden Rule: Use the right tool for the right job, không phải "cool" nhất.

6.1 gRPC Best Practices

protobuf
// ✅ GOOD: Specific message types
message CreateUserRequest {
  string name = 1;
  string email = 2;
}

message CreateUserResponse {
  User user = 1;
  Error error = 2;  // Explicit error handling
}

// ❌ BAD: Generic catch-all
message GenericRequest {
  map<string, string> data = 1;
}

6.2 Contract-First Development Flow

┌─────────────────────────────────────────────────────────────────────────┐
│                    CONTRACT-FIRST WORKFLOW                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   1. DESIGN PHASE                                                        │
│   ═══════════════                                                        │
│   Team discusses & writes .proto file together                          │
│                                                                          │
│   2. REVIEW                                                              │
│   ═════════                                                              │
│   PR review for .proto changes                                          │
│   Breaking changes? Bump version                                        │
│                                                                          │
│   3. GENERATE                                                            │
│   ══════════                                                             │
│   protoc --go_out=. --go-grpc_out=. user.proto                          │
│   protoc --python_out=. --grpc_python_out=. user.proto                  │
│                                                                          │
│   4. IMPLEMENT                                                           │
│   ════════════                                                           │
│   Server: Implement generated interface                                 │
│   Client: Use generated stubs                                           │
│                                                                          │
│   5. TEST                                                                │
│   ═══════                                                                │
│   Contract tests ensure compatibility                                   │
│                                                                          │
│   ✅ Type-safe across languages                                         │
│   ✅ Breaking changes caught at compile time                            │
│   ✅ Auto-generated documentation                                       │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

7. Scenario Quiz

Câu hỏi 1: Startup API Design

Bạn build MVP cho startup. Team có 3 devs, product đang thay đổi hàng tuần. Mobile app cần data từ 5 endpoints khác nhau cho 1 màn hình.

Bạn chọn REST, gRPC, hay GraphQL? Tại sao?

👁️ Xem đáp án

Đáp án: GraphQL (hoặc REST với BFF pattern)

Phân tích:

  • Team size nhỏ → Learning curve của gRPC không worth it
  • Product thay đổi nhanh → GraphQL flexibility cho phép frontend tự adapt
  • 5 endpoints cho 1 màn hình → Under-fetching problem → GraphQL solves this
  • Mobile app → Bandwidth sensitivity → GraphQL giảm over-fetching

Alternatives:

  • REST + BFF (Backend for Frontend): Tạo /api/mobile/home-screen endpoint
  • REST + Composite endpoints: Merge nhiều resources vào 1 response

Không chọn gRPC vì:

  • Browser/Mobile không support native
  • Setup complexity cho small team
  • Debugging khó hơn trong early stage

Câu hỏi 2: High-throughput Internal Services

Bạn có 50 microservices trong Kubernetes cluster. Mỗi service gọi 10 services khác. Latency requirement: P99 < 10ms cho internal calls.

Bạn chọn protocol nào?

👁️ Xem đáp án

Đáp án: gRPC

Lý do:

  • P99 < 10ms → REST JSON parsing overhead quá cao
  • 50 services, polyglot → gRPC code generation ensures type safety
  • Internal only → Không cần browser compatibility
  • High call volume → Binary encoding saves bandwidth

Implementation notes:

yaml
# Service mesh (Istio/Linkerd) với gRPC load balancing
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
spec:
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    connectionPool:
      http:
        h2UpgradePolicy: UPGRADE  # HTTP/2 for gRPC

Monitoring:

  • gRPC errors → Standardized status codes
  • Latency → Interceptors for metrics
  • Tracing → OpenTelemetry integration

8. Tiếp theo

Bây giờ bạn đã hiểu về Synchronous Communication, hãy khám phá:

👉 Asynchronous Messaging →

Chúng ta sẽ so sánh Kafka vs RabbitMQ - và hiểu khi nào dùng message queue thay vì synchronous calls.