gRPC vs GraphQL vs REST vs tRPC 2026 - Chọn Đúng Protocol Giao Tiếp cho Microservices và Frontend-Backend Contract

Posted on: 4/16/2026 12:12:20 PM

Table of contents

  1. 1. Chọn protocol giao tiếp năm 2026 — một quyết định kiến trúc, không phải sở thích
  2. 2. Biên niên sử API — vì sao đến 2026 vẫn còn bốn ông lớn
  3. 3. REST + OpenAPI 3.1 — sự hồi sinh nhờ schema-first
    1. OpenAPI 3.1 cải tiến lớn nhất
    2. 3.1. Điểm mạnh và điểm yếu của REST năm 2026
  4. 4. gRPC, Connect và Protobuf Editions — ngôn ngữ của tầng service-to-service
    1. 4.1. Protobuf Editions — chấm dứt chia đôi proto2/proto3
    2. 4.2. Connect RPC — gRPC không cần HTTP/2
    3. 4.3. Buf breaking — tự động phát hiện breaking change
    4. 4.4. Điểm mạnh và điểm yếu của gRPC năm 2026
  5. 5. GraphQL Federation v2 và Apollo Router — BFF ở quy mô công ty
    1. 5.1. Federation v2 khác gì v1
    2. 5.2. Apollo Router — Rust thay cho Node.js gateway
      1. GraphQL và caching — một điểm đau kinh điển
    3. 5.3. Authorization và rate limit — cần thiết kế riêng
    4. 5.4. Điểm mạnh và điểm yếu của GraphQL năm 2026
  6. 6. tRPC v11 — không schema, không codegen, chỉ có TypeScript
    1. 6.1. tRPC v11 có gì mới
    2. 6.2. Điểm mạnh và điểm yếu của tRPC năm 2026
  7. 7. So sánh trực tiếp — một bảng gói gọn quyết định
  8. 8. Ma trận quyết định — chọn theo bối cảnh
    1. Kiến trúc hybrid là chuẩn mực
  9. 9. Benchmark và con số thực tế
  10. 10. Contract evolution — nơi nhiều team vấp ngã nhất
  11. 11. Authentication, authorization, observability — xương sống phải có dù chọn gì
    1. Đừng nhầm protocol với kiến trúc observability
  12. 12. Ví dụ kiến trúc thực tế — e-commerce đa protocol
  13. 13. Những pitfall phải tránh khi migrate
  14. 14. Định hướng — đặt protocol trong chiến lược API của tổ chức
  15. 15. Tài liệu tham khảo

1. Chọn protocol giao tiếp năm 2026 — một quyết định kiến trúc, không phải sở thích

Mỗi lần mình review kiến trúc cho một nhóm microservices mới, câu hỏi lặp lại nhiều nhất vẫn là: "team nên dùng REST, gRPC, GraphQL hay tRPC?". Câu trả lời "tuỳ" nghe thì đúng, nhưng bỏ qua ba năm gần đây bản thân mỗi protocol đã thay đổi rất nhiều: REST có OpenAPI 3.1 + JSON Schema 2020-12, gRPC đã bước sang Protobuf Editions và Connect-RPC tương thích HTTP/JSON, GraphQL có Federation v2 với Apollo Router viết bằng Rust, còn tRPC v11 đã trở thành chuẩn de-facto cho full-stack TypeScript. Năm 2026 không còn là "REST vs GraphQL" như năm 2018 — mà là một lưới trade-off đa chiều, phụ thuộc vào biên (edge), tầng (service-to-service), client (web/mobile/máy chủ) và hợp đồng (contract evolution) mà hệ thống phải sống chung.

Bài viết này so sánh bốn protocol phổ biến nhất cho backend API năm 2026 dưới ba góc nhìn: hợp đồng và schema evolution, mô hình truyền tải (request/response, streaming, subscription), và chi phí vận hành (latency, payload size, observability). Mình sẽ đi sâu vào những tính năng mới của từng nhóm, đưa ra benchmark thực tế từ các bài so sánh công khai, và quan trọng nhất là dựng sẵn một ma trận quyết định để bạn có thể bê về team mà không cần sa lầy vào tranh cãi tôn giáo nữa.

4Protocol được so sánh: REST + OpenAPI, gRPC + Connect, GraphQL Federation v2, tRPC v11
~70%Giảm payload trung bình khi chuyển REST JSON sang gRPC Protobuf cho inter-service
3xApollo Router Rust nhanh hơn Apollo Gateway JS trong Federation benchmark chính thức
1Source of truth: schema là hợp đồng, không phải code, không phải comment

2. Biên niên sử API — vì sao đến 2026 vẫn còn bốn ông lớn

Để trả lời câu hỏi "chọn gì", cần nhìn lại mỗi protocol sinh ra để giải bài toán gì. Lịch sử không phải để hoài niệm, mà để thấy ràng buộc thiết kế nào còn đúng và ràng buộc nào đã lỗi thời.

2000 — REST (Roy Fielding)
Luận án tiến sĩ của Roy Fielding mô tả REST như một kiểu kiến trúc cho hệ thống phân tán: stateless, cacheable, uniform interface. REST không phải một đặc tả — đó là lý do vì sao 25 năm sau vẫn còn tranh cãi "REST thật sự là gì".
2010-2014 — JSON thống trị web
AJAX, single-page app, mobile native đẩy JSON thành format chuẩn. SOAP/XML-RPC dần biến mất ở tầng public API. REST + JSON trở thành mặc định cho Web 2.0.
2015 — gRPC (Google) + Protocol Buffers 3
Google công bố gRPC, rút ruột từ Stubby nội bộ. HTTP/2 multiplexing, Protobuf binary, 4 kiểu streaming (unary, server, client, bidi). Chiếm ngay lĩnh vực inter-service ở các công ty hyperscale.
2015 — GraphQL (Facebook/Meta)
Meta mở nguồn GraphQL sau nhiều năm dùng nội bộ cho mobile app. Giải quyết over-fetching và under-fetching, đặc biệt cho client mobile băng thông yếu. Apollo và Relay nhanh chóng thành ecosystem chính.
2017 — OpenAPI 3.0
Swagger đổi tên thành OpenAPI, chuẩn hoá schema cho REST. Lần đầu cộng đồng REST có contract-first đúng nghĩa. Codegen TypeScript/C#/Go nở rộ.
2020 — tRPC ra đời
Alex Johansson tạo tRPC trong bối cảnh Next.js + TypeScript chiếm ưu thế. Ý tưởng đơn giản: nếu client và server đều TypeScript, bỏ luôn schema trung gian, để type của server infer thẳng sang client.
2022-02 — Apollo Federation v2
Federation v2 thay thế "gateway + schema stitching" bằng composition graph thực thụ. Apollo Router viết bằng Rust thay cho Apollo Gateway Node.js, latency P99 giảm mạnh.
2023-05 — Connect RPC (Buf)
Buf Technologies phát hành Connect RPC: dùng Protobuf schema như gRPC nhưng transport over HTTP/1.1, HTTP/2, HTTP/3, JSON hoặc binary. Xóa bỏ rào cản "gRPC không chạy qua trình duyệt".
2024 — OpenAPI 3.1 + JSON Schema 2020-12
OpenAPI 3.1 hoà với JSON Schema 2020-12 chính thức, chấm dứt chia rẽ schema. Moon Walk draft của 4.0 bắt đầu RFC cho hyperschema và async.
2024-2025 — Protobuf Editions
Protobuf Editions thay thế proto2/proto3 bằng một mô hình evolution thống nhất qua feature set. Edition 2023 ổn định, Edition 2024 bổ sung safer defaults cho presence và unknown fields.
2025-Q4 — tRPC v11
tRPC v11 ổn định, hỗ trợ React Server Components, subscription over HTTP streaming, adapter chính thức cho Hono, Elysia, Fastify. Sử dụng trong 80%+ startup T3-stack.
2026-Q1 — Trạng thái hiện tại
Bốn protocol cùng tồn tại, chiếm các lát cắt khác nhau: REST + OpenAPI cho public API và BFF, gRPC/Connect cho inter-service và AI serving, GraphQL Federation cho BFF tổng hợp nhiều service, tRPC cho full-stack TS.

3. REST + OpenAPI 3.1 — sự hồi sinh nhờ schema-first

REST thường bị xem là "cũ", nhưng phiên bản REST năm 2026 khác xa REST 2016. Khác biệt chính nằm ở OpenAPI 3.1: spec này hoà hoàn toàn với JSON Schema 2020-12, cho phép mô tả discriminator polymorphism, nullability, const, oneOf/allOf một cách chặt chẽ, và hỗ trợ webhook ở cấp first-class thay vì phải patch ngoài như trước.

Điểm mạnh lớn nhất của REST + OpenAPI hiện nay là hệ sinh thái công cụ: mọi API gateway (AWS API Gateway, Azure API Management, Kong, Envoy, YARP) đều hiểu OpenAPI, mọi ngôn ngữ đều có codegen (NSwag cho .NET, openapi-typescript cho TS, oapi-codegen cho Go, orval/kiota cho client SDK), mọi docs generator đều render được (Redoc, Scalar, Stoplight). Khi cần public API cho đối tác B2B hay xây dựng SDK cho nhiều ngôn ngữ, REST + OpenAPI là lựa chọn ít rủi ro nhất.

OpenAPI 3.1 cải tiến lớn nhất

Hoà với JSON Schema 2020-12 có nghĩa là schema OpenAPI có thể dùng trực tiếp với Ajv, Zod (qua zod-to-json-schema), FluentValidation (qua bộ converter của Microsoft), hoặc Pydantic 2. Một schema, nhiều nơi validate — giảm đáng kể drift giữa API doc và validator thực tế.

Ở phía .NET 10, ASP.NET Core đã tích hợp OpenAPI 3.1 mặc định với package Microsoft.AspNetCore.OpenApi. Dưới đây là một Minimal API tiêu chuẩn năm 2026:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi("v1", opt =>
{
    opt.AddDocumentTransformer((doc, ctx, ct) =>
    {
        doc.Info.Title = "Catalog API";
        doc.Info.Version = "v1";
        return Task.CompletedTask;
    });
});
builder.Services.AddProblemDetails();

var app = builder.Build();
app.MapOpenApi();

app.MapGet("/products/{id:guid}", async (Guid id, ICatalogStore store, CancellationToken ct) =>
{
    var product = await store.FindAsync(id, ct);
    return product is null
        ? Results.Problem(statusCode: 404, title: "Product not found")
        : Results.Ok(product);
})
.WithName("GetProductById")
.Produces<ProductDto>(200)
.ProducesProblem(404);

app.Run();

Điểm đáng lưu ý: ProblemDetails (RFC 9457) đã thành chuẩn lỗi mặc định cho REST 2026. Thay vì trả JSON tự chế, mọi lỗi 4xx/5xx đều nên tuân theo schema application/problem+json — giúp client xử lý lỗi nhất quán qua nhiều service.

3.1. Điểm mạnh và điểm yếu của REST năm 2026

  • Mạnh: universal — mọi CDN, WAF, caching layer, browser dev tool đều hiểu. Cacheability qua HTTP header là trời phú. OpenAPI 3.1 chặt về schema. Biểu diễn resource rõ ràng, dễ debug bằng curl.
  • Mạnh: HTTP status code mang ý nghĩa ngữ nghĩa; CDN/WAF/proxy sử dụng được ngay — gRPC không có lợi thế này.
  • Yếu: không có chuẩn binary — JSON parse/serialize là cost lớn ở scale cao. MessagePack/CBOR tồn tại nhưng hiếm khi được tooling hỗ trợ trọn vẹn.
  • Yếu: streaming yếu. SSE và chunked transfer có thể làm, nhưng không có abstraction cấp framework như gRPC streaming.
  • Yếu: contract evolution thủ công — phải tự đặt versioning (URI, header, hoặc media type), không có tooling tự phát hiện breaking change mạnh như buf breaking.

4. gRPC, Connect và Protobuf Editions — ngôn ngữ của tầng service-to-service

gRPC không phải protocol duy nhất dùng Protobuf, nhưng là phổ biến nhất. Năm 2026 có hai thay đổi lớn: Protobuf Editions thay thế proto2/proto3, và Connect RPC trở thành cách tiếp cận "gRPC không đau" cho trình duyệt và service không chịu nổi HTTP/2 gRPC thuần.

4.1. Protobuf Editions — chấm dứt chia đôi proto2/proto3

Proto2 có presence rõ ràng (biết field có được set hay không) nhưng cú pháp rối. Proto3 gọn hơn nhưng default value bị conflate với "unset", gây bug tinh vi. Editions thống nhất cả hai qua feature set: mỗi file khai báo edition = "2024", feature như features.field_presence có thể đổi giữa IMPLICIT, EXPLICIT, LEGACY_REQUIRED. Migration từ proto3 sang Editions tự động qua protoc --edition_defaults.

edition = "2024";
package catalog.v1;

import "google/protobuf/timestamp.proto";

option features.field_presence = EXPLICIT;

message Product {
  string id = 1;
  string name = 2;
  int64 price_minor_units = 3;
  google.protobuf.Timestamp created_at = 4;
  Status status = 5;

  enum Status {
    STATUS_UNSPECIFIED = 0;
    STATUS_DRAFT = 1;
    STATUS_PUBLISHED = 2;
    STATUS_ARCHIVED = 3;
  }
}

service CatalogService {
  rpc GetProduct(GetProductRequest) returns (Product);
  rpc ListProducts(ListProductsRequest) returns (stream Product);
  rpc UploadMedia(stream UploadMediaChunk) returns (UploadMediaResponse);
}

4.2. Connect RPC — gRPC không cần HTTP/2

Buf Technologies phát hành Connect năm 2023 và đến 2026 đã thành lựa chọn mặc định cho nhiều team. Connect dùng cùng file .proto, cùng codegen tool, nhưng tạo ra client và server hiểu được ba transport: gRPC thuần (HTTP/2), gRPC-Web (tương thích trình duyệt), và Connect (HTTP/1.1 hoặc HTTP/2 với JSON/Protobuf payload). Điều này cho phép cùng một service phục vụ cả microservice nội bộ lẫn frontend web mà không cần envoy-based proxy chuyển đổi như xưa.

graph LR
    subgraph Client["Client layer"]
        B["Browser"]
        M["Mobile"]
        S["Service"]
    end
    subgraph Transport["Transport"]
        C1["Connect JSON HTTP/1.1"]
        C2["gRPC-Web HTTP/2"]
        C3["gRPC HTTP/2"]
    end
    subgraph Server["Connect server (Go/Node/Java)"]
        H["Unified handler"]
    end
    B --> C1
    M --> C2
    S --> C3
    C1 --> H
    C2 --> H
    C3 --> H
Một server duy nhất phục vụ cả ba transport, cùng một file .proto

4.3. Buf breaking — tự động phát hiện breaking change

Công cụ buf có rule set breaking: chạy trong CI để so schema PR với branch chính, nó tự chặn mọi thay đổi phá hợp đồng (xoá field, đổi số field, đổi type, đổi semantic của enum). Đây là thứ REST + OpenAPI vẫn thiếu chặt chẽ; kết hợp với buf lintbuf format, contract evolution trong gRPC trở thành quy trình cơ học chứ không phụ thuộc kỷ luật developer.

4.4. Điểm mạnh và điểm yếu của gRPC năm 2026

  • Mạnh: binary Protobuf giảm payload 60-80% so với JSON REST ở nội bộ hyperscale. Streaming cả 4 kiểu ở cấp framework.
  • Mạnh: contract evolution tự động hoá qua buf breaking + Editions — khó lỡ tay phá compat.
  • Mạnh: codegen đa ngôn ngữ chính thức (Go, Java, C++, Python, C#, Rust, Node, Kotlin, Swift, Dart) — không có protocol nào đa ngôn ngữ bằng.
  • Yếu: thuần gRPC yêu cầu HTTP/2 đầu cuối — WAF, CDN, API Gateway cổ không support. Connect giải quyết được nhưng phải chọn.
  • Yếu: debug qua curl không khả dụng ngoài trừ Connect JSON mode. Cần grpcurl, buf curl, hoặc reflection server.
  • Yếu: HTTP caching layer gần như vô dụng — cache phải làm ở tầng application. WAF rule khó viết vì payload binary.

5. GraphQL Federation v2 và Apollo Router — BFF ở quy mô công ty

GraphQL năm 2018 hay bị lạm dụng: team nào cũng wrap REST thành GraphQL "cho sang", nhưng không giải quyết bài toán over/under-fetching cốt lõi, lại thêm gánh nặng N+1, authorization khó khoanh vùng, caching khó ở cấp CDN. Tuy nhiên, năm 2026 GraphQL đã thu hẹp về đúng lát cắt mạnh nhất của nó: Federation — một graph tổng hợp từ nhiều subgraph, thích hợp cho tổ chức có hàng chục đến hàng trăm service và nhiều BU cần chung view cho client.

5.1. Federation v2 khác gì v1

Federation v1 (2019) dùng schema stitching: gateway hợp nhất schema bằng cách directive @key, @extends. Federation v2 (2022) tiến lên composition thực thụ: Rover CLI (công cụ chính thức của Apollo) biên dịch các subgraph thành một supergraph schema duy nhất, kiểm tra trước mọi mâu thuẫn (conflict field, shareable, tag, interface object). Nếu một PR trong subgraph A phá hợp đồng của subgraph B, CI từ chối trước khi deploy.

# subgraph products
type Product @key(fields: "id") {
  id: ID!
  name: String!
  priceMinorUnits: Int!
}

# subgraph inventory
extend type Product @key(fields: "id") {
  id: ID! @external
  stock: Int!
  warehouse: Warehouse!
}

type Warehouse {
  id: ID!
  location: String!
}

Supergraph schema sau khi composition sẽ có Product đầy đủ name, priceMinorUnits, stock, warehouse. Client chỉ cần biết một graph, không biết cấu trúc service nền dưới.

5.2. Apollo Router — Rust thay cho Node.js gateway

Apollo Router viết bằng Rust, thay thế Apollo Gateway cũ viết Node.js. Benchmark chính thức của Apollo cho thấy Router xử lý 3x throughput ở P99 với bộ nhớ chỉ 1/3. Router hỗ trợ query planning thông minh hơn: batching request tới subgraph, dedup field selection, và cache kết quả composition. Đối với workload 100k req/s tầm trung, Router chạy ổn định với một vài core, không phải scale ngang như Node Gateway từng phải làm.

GraphQL và caching — một điểm đau kinh điển

Vì query nằm trong POST body, CDN và reverse proxy không cache được trực tiếp. Apollo Router hỗ trợ Automatic Persisted Queries (APQ): client gửi hash của query, server lookup; CDN sau đó có thể cache theo GET URL + hash. Nếu team bạn không dùng APQ, đừng kỳ vọng CDN cache.

5.3. Authorization và rate limit — cần thiết kế riêng

GraphQL không có "endpoint" để rate-limit theo request count, vì một query có thể nặng (nested 10 tầng) hoặc nhẹ (1 field). Chuẩn mực 2026 là query cost analysis: gán cost cho mỗi field, tổng hợp theo AST của query, từ chối nếu vượt budget. Các field-level auth dùng @requires, @policy (Cedar hoặc OPA) thay vì middleware truyền thống.

5.4. Điểm mạnh và điểm yếu của GraphQL năm 2026

  • Mạnh: một graph cho mọi client — BFF cho web, mobile, partner API không cần maintain nhiều endpoint.
  • Mạnh: Federation v2 cho phép chia ownership theo domain team, không cần monorepo schema.
  • Mạnh: client explorer (Apollo Sandbox, GraphiQL) cực mạnh cho DX.
  • Yếu: N+1 mặc định; cần DataLoader và schema design kỷ luật.
  • Yếu: caching CDN không tự nhiên, cần APQ + thiết kế. Auth và rate-limit phải làm ở cấp field.
  • Yếu: streaming subscription qua WebSocket/SSE còn nhiều lựa chọn (graphql-ws, sse), chưa ổn định bằng gRPC streaming.

6. tRPC v11 — không schema, không codegen, chỉ có TypeScript

tRPC là con đẻ của hệ sinh thái TypeScript full-stack. Triết lý cốt lõi: nếu frontend và backend cùng viết TypeScript và cùng codebase (monorepo), thì tại sao phải định nghĩa schema trung gian? Server export type của router, client import type đó, TypeScript compiler làm cầu type-safe hai đầu. Không có codegen, không có file .proto hay .graphql, chỉ có TypeScript.

// server/router.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';

const t = initTRPC.create();

export const appRouter = t.router({
  product: t.router({
    byId: t.procedure
      .input(z.object({ id: z.string().uuid() }))
      .query(async ({ input }) => {
        return await db.product.findUnique({ where: { id: input.id } });
      }),
    list: t.procedure
      .input(z.object({ cursor: z.string().optional(), take: z.number().max(100) }))
      .query(async ({ input }) => {
        // ...
      }),
  }),
});

export type AppRouter = typeof appRouter;
// client/index.ts
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/router';

const trpc = createTRPCClient<AppRouter>({
  links: [httpBatchLink({ url: '/api/trpc' })],
});

const product = await trpc.product.byId.query({ id: '...' });
//     ^? Inferred: Product | null — không codegen, không schema trung gian

6.1. tRPC v11 có gì mới

  • React Server Components: server-side call không cần wrap router, có thể gọi thẳng caller.product.byId() bên trong RSC.
  • Streaming response: procedure có thể trả AsyncIterable, client nhận dần — không phải WebSocket nhưng đủ cho long-running query hoặc token streaming LLM.
  • Adapter đầy đủ: Hono, Elysia, Fastify, Express, Next.js, Nuxt (qua community), SvelteKit, SolidStart.
  • Subscription over SSE thay WebSocket, đơn giản hoá infra edge.

6.2. Điểm mạnh và điểm yếu của tRPC năm 2026

  • Mạnh: DX vượt trội cho team TS full-stack — không có bước codegen, refactor server kéo theo client tự động.
  • Mạnh: Zod/Valibot schema kiêm luôn validator + type infer, giảm boilerplate.
  • Mạnh: Bundle nhỏ, không runtime nặng, latency overhead gần như không có so với REST JSON.
  • Yếu: chỉ type-safe trong TypeScript. Client Go/Java/Python phải tự dùng REST adapter — mất gần hết lợi ích.
  • Yếu: không có contract rõ ràng cho đối tác bên ngoài. tRPC không phù hợp làm public API.
  • Yếu: phụ thuộc mạnh vào monorepo và build system (tsc/SWC đủ nhanh). Microfrontend đa team, đa repo thì tRPC trở nên khó quản.

7. So sánh trực tiếp — một bảng gói gọn quyết định

Tiêu chíREST + OpenAPI 3.1gRPC / ConnectGraphQL Federation v2tRPC v11
Schema định dạngOpenAPI YAML/JSON + JSON SchemaProtobuf .proto (Editions)GraphQL SDLTypeScript types (không schema)
PayloadJSON (có thể CBOR/MessagePack)Protobuf binary / JSON (Connect)JSONJSON (hoặc superjson)
TransportHTTP/1.1, HTTP/2, HTTP/3HTTP/2 (Connect hỗ trợ HTTP/1.1)HTTP/1.1, WebSocket (subscription)HTTP/1.1, SSE, WS tuỳ adapter
StreamingSSE, chunked4 kiểu: unary/server/client/bidiSubscription qua graphql-ws/sseAsyncIterable over HTTP stream
Codegen clientopenapi-typescript, NSwag, orval, kiotaprotoc + buf generategraphql-codegenKhông cần — type inference
Browser nativeCó (fetch)gRPC-Web / ConnectCó (fetch)Có (fetch)
Caching CDNRất tốt (GET + header)KémKém (cần APQ)Trung bình (qua batch link GET)
Contract evolutionThủ công + versioning URL/headerbuf breaking tự độngRover composition checkType compiler báo lỗi
PolyglotXuất sắcXuất sắcTốtChỉ TypeScript
Phù hợp choPublic API, B2B, BFFInter-service, AI serving, performance-criticalBFF nhiều domain, multi-clientFull-stack TS monorepo

8. Ma trận quyết định — chọn theo bối cảnh

Không có protocol "tốt nhất". Có protocol phù hợp nhất với một bối cảnh cụ thể. Mình đã dùng ma trận dưới đây trong review kiến trúc cho nhiều team, phần lớn quyết định chỉ mất 30 phút nếu trả lời thành thật bốn câu:

graph TD
    Start["API mới cần gì?"] --> Q1{"Public API cho
đối tác ngoài?"} Q1 -- Có --> REST["REST + OpenAPI 3.1
Lý do: universal, cache CDN, SDK đa ngôn ngữ"] Q1 -- Không --> Q2{"Inter-service trong
private network?"} Q2 -- Có --> Q3{"Cần streaming
hai chiều?"} Q3 -- Có --> GRPC["gRPC / Connect
Lý do: Protobuf binary, streaming native"] Q3 -- Không --> Q3a{"Polyglot team?"} Q3a -- Có --> GRPC2["gRPC / Connect"] Q3a -- Không --> Q3b{"Full-stack TS
cùng monorepo?"} Q3b -- Có --> TRPC["tRPC v11
Lý do: zero schema, DX vượt trội"] Q3b -- Không --> REST2["REST + OpenAPI"] Q2 -- Không --> Q4{"Nhiều client
cần view tổng hợp
nhiều domain?"} Q4 -- Có --> GQL["GraphQL Federation v2
Lý do: composition schema, DX client mạnh"] Q4 -- Không --> REST3["REST + OpenAPI"]
Cây quyết định rút gọn cho API mới năm 2026

Kiến trúc hybrid là chuẩn mực

Thực tế production, rất ít hệ thống dùng duy nhất một protocol. Pattern phổ biến 2026: REST + OpenAPI ở edge (public API), gRPC ở tầng inter-service, GraphQL Federation làm BFF cho web/mobile app phức tạp, tRPC cho admin dashboard hoặc startup TS-only. Bài toán là chọn đúng lát cắt, không phải chọn "one true protocol".

9. Benchmark và con số thực tế

Benchmark protocol luôn gây tranh cãi vì phụ thuộc vào workload, hardware, payload shape. Mình tổng hợp con số từ ba nguồn công khai thường được trích dẫn nhất năm 2025-2026:

WorkloadREST JSONgRPC ProtobufGraphQLtRPC (JSON)
Payload 1 entity ~1KB~1.1 KB~0.3-0.4 KB~1.1 KB~1.1 KB
Latency P50 (same VPC)1.8 ms0.9 ms2.2 ms1.8 ms
Latency P9914 ms6 ms18 ms (router Rust)14 ms
Throughput (1 core server)~18k req/s~45k req/s~12k req/s~18k req/s
CPU per reqbaseline~0.4x baseline~1.3x baselinebaseline

Con số mang tính tham khảo cho workload CRUD đơn giản; workload thực tế (nested graph 10 tầng, aggregation analytical, binary upload) có thể ngược lại hoàn toàn. Điều rút ra ổn định qua mọi benchmark: gRPC luôn nhanh nhất cho inter-service vì payload binary + HTTP/2 multiplexing; GraphQL thường bị nặng hơn REST do overhead AST parse + resolver pipeline, nhưng Apollo Router Rust đã thu hẹp khoảng cách đáng kể; REST và tRPC gần như tương đương vì cùng JSON-over-HTTP, chỉ khác ở layer type safety.

10. Contract evolution — nơi nhiều team vấp ngã nhất

Sau một năm, API nào cũng phải tiến hoá. Khác biệt giữa protocol nằm ở chỗ: có tooling tự động chặn breaking change hay không, và nếu có thì ở tầng nào.

  • REST + OpenAPI: thủ công. oasdiffopenapi-diff có thể so hai version, nhưng cần tự đặt vào CI, tự định nghĩa breaking rule. Versioning thường qua URL (/v1, /v2) hoặc header (Accept: application/vnd.api+json; version=2). Pitfall: quên update spec sau khi sửa code, dẫn đến drift.
  • gRPC: tự động. buf breaking chạy trong CI với rule set WIRE_JSON hoặc WIRE, chặn mọi thay đổi phá compat. Protobuf không cần versioning URL — deprecation field qua [deprecated = true], xoá field sau chu kỳ grace.
  • GraphQL: rất tự động qua Apollo Rover composition check và @tag/@deprecated. Không có versioning; evolution qua adding field mới và deprecate field cũ. Client tự chọn field cần, không có "breaking" theo nghĩa REST.
  • tRPC: compiler là tooling. Nếu server xoá procedure hay đổi type, client TS build fail ngay. Mạnh trong monorepo, yếu khi client và server deploy độc lập vì compile-time check không ngăn được runtime mismatch giữa các version đã deploy.

11. Authentication, authorization, observability — xương sống phải có dù chọn gì

Mọi protocol đều cần ba thứ: auth ở biên, audit log, và tracing xuyên suốt. Năm 2026 chuẩn chung là:

  • OAuth 2.1 / OIDC cho auth, JWT hoặc opaque token đều được miễn chuẩn hoá qua introspection. mTLS ở tầng service-to-service cho gRPC.
  • OpenTelemetry cho tracing, metric, log. Tất cả bốn protocol đều có instrumentation chính thức; W3C Trace Context header traceparent truyền qua metadata gRPC, header HTTP cho REST/GraphQL/tRPC.
  • Fine-grained authorization qua OPA/Cedar hoặc policy-as-code. GraphQL có field-level authorization native qua directive; REST/gRPC/tRPC cần middleware.

Đừng nhầm protocol với kiến trúc observability

Nhiều team chọn gRPC vì "nhanh hơn" nhưng lại để log dưới dạng string không structured, không có trace context. Protocol chỉ là wire format — observability phụ thuộc vào OpenTelemetry, structured logging, và semantic convention. Chuyển từ REST sang gRPC mà không đồng thời nâng observability thì chỉ đổi protocol chứ không tăng chất lượng vận hành.

12. Ví dụ kiến trúc thực tế — e-commerce đa protocol

Để khép lại, dưới đây là một kiến trúc e-commerce điển hình năm 2026 kết hợp cả bốn protocol, mỗi protocol đứng đúng lát cắt mạnh nhất:

graph TD
    Client1["Web SPA Vue/React"] --> BFF
    Client2["Mobile app iOS/Android"] --> BFF
    Partner["Partner B2B"] --> PublicAPI["Public REST API
OpenAPI 3.1"] AdminUI["Admin dashboard
Next.js + TS"] --> TRPC["tRPC v11
trong monorepo"] BFF["GraphQL Federation Router
Apollo Router Rust"] BFF --> Products["Products subgraph"] BFF --> Inventory["Inventory subgraph"] BFF --> Orders["Orders subgraph"] BFF --> Users["Users subgraph"] PublicAPI --> Products PublicAPI --> Orders Products --> PricingSvc["Pricing Service
gRPC"] Orders --> PaymentSvc["Payment Service
gRPC + Connect"] Orders --> FulfillmentSvc["Fulfillment Service
gRPC streaming"] Inventory --> WarehouseSvc["Warehouse Service
gRPC streaming"] TRPC --> Products TRPC --> Orders
Kiến trúc e-commerce đa protocol — mỗi protocol ở đúng lát cắt

Phân tích:

  • Public REST + OpenAPI: đối tác B2B tích hợp qua SDK generate từ OpenAPI, mọi ngôn ngữ. CDN cache được GET product catalog.
  • GraphQL Federation: BFF cho web/mobile, tổng hợp data từ 4 domain team. Client chỉ gọi một endpoint, mobile yêu cầu đúng field cần.
  • gRPC inter-service: giao tiếp giữa subgraph và service nền (Pricing, Payment, Fulfillment, Warehouse). Streaming cho update tồn kho real-time, binary Protobuf tiết kiệm băng thông.
  • tRPC admin: admin dashboard và backend admin cùng monorepo, TypeScript full-stack. Ship feature admin nhanh không cần maintain thêm schema.

13. Những pitfall phải tránh khi migrate

  • Nhảy từ REST sang gRPC chỉ vì hot: nếu service hiện tại chạy êm dưới 10k req/s, lợi ích binary payload không bù được chi phí re-tool observability, WAF, dev environment. gRPC thắng rõ ở scale cao hoặc cần streaming, ít hiệu quả ở CRUD đơn giản.
  • Dùng GraphQL như REST: một graph một endpoint, rồi viết resolver trả toàn bộ entity. Kết quả: mất lợi thế over-fetching, còn mang thêm nặng AST. GraphQL chỉ giá trị khi schema phản ánh graph thật và client lựa field.
  • tRPC cho public API: đối tác bên ngoài không có type inference. Phải tự gen REST adapter, mất hết lợi thế. tRPC là nội bộ TS — để ngoài đường biên là bài toán khó.
  • OpenAPI drift: spec và code lệch nhau. Chuẩn mực 2026 là code-first sinh OpenAPI (ASP.NET Core, FastAPI, Hono) — schema phản ánh runtime thật, không còn spec viết tay lạc hậu.
  • Quên backward compat: mọi protocol đều có cơ chế tương thích ngược, nhưng chỉ có gRPC Editions + buf breaking là bắt buộc; các protocol khác phụ thuộc kỷ luật team. Đặt CI check breaking từ ngày đầu, không đợi sự cố.

14. Định hướng — đặt protocol trong chiến lược API của tổ chức

Mình thường khuyên team viết một tài liệu ngắn gọn tên "API Style Guide" khoảng 3-5 trang, cố định: (1) public API chuẩn nào (REST + OpenAPI 3.1, error = ProblemDetails); (2) inter-service chuẩn nào (gRPC với Connect fallback); (3) BFF chuẩn nào (GraphQL Federation hoặc REST BFF tuỳ scale); (4) frontend admin chuẩn nào (tRPC nếu TS, REST nếu đa stack). Tài liệu này không cố tạo ràng buộc cứng, nhưng mỗi khi team mới spin up service mới, họ có default lựa chọn và không phải tranh cãi lại từ đầu.

Năm 2027 và xa hơn, xu hướng mà mình quan sát là hội tụ về schema-first: OpenAPI 3.1 + JSON Schema và Protobuf Editions đang giảm dần khoảng cách, tRPC có thể sinh OpenAPI từ router, GraphQL có thể generate từ Protobuf (qua protoc-gen-graphql). Không có lý do gì năm 2030 team phải chọn giữa "type safety monorepo" và "contract rõ ràng cho đối tác" — nhiều khả năng cả hai sẽ nằm chung trong một toolchain. Nhưng cho đến khi đó, bốn protocol trên vẫn chiếm bốn lát cắt riêng, và hiểu rõ biên giới của chúng là kỹ năng kiến trúc cơ bản cần có.

15. Tài liệu tham khảo