Bỏ qua

ADR-0003: Thêm lớp GraphQL additive tại api-gateway (song song với REST)

  • Status: Accepted
  • Date: 2026-07-24
  • Supersedes: —
  • Superseded by: —

Bối cảnh

api-gateway vốn chỉ expose REST (proxy sang các service nội bộ). Nhu cầu phát sinh: FE cần query linh hoạt dữ liệu tổng hợp từ nhiều service (VD: field + reviews + availability + owner dashboard) mà không muốn gọi nhiều REST endpoint rồi tự ghép ở client, dẫn tới over-fetch/under-fetch và nhiều round-trip. Đồng thời không muốn phá vỡ các client REST hiện có.

Quyết định

Thêm một lớp GraphQL bổ sung (additive) tại api-gateway, expose duy nhất tại POST /graphql, dùng @nestjs/graphql + @apollo/server:

  • REST endpoint giữ nguyên, không bị thay thế — GraphQL "sits alongside" (theo docs/guides/GRAPHQL_API_GUIDE_EN.md)
  • Dùng DataLoader (apps/api-gateway/src/graphql/loaders/create-loaders.ts) để batch/cache request tới các service nội bộ trong 1 request GraphQL, tránh N+1
  • Auth tái dùng Keycloak JWT hiện có (Authorization: Bearer <keycloak-jwt>)
  • graphql-depth-limit để chặn query quá sâu (bảo vệ khỏi truy vấn độc hại)
  • Custom throttler riêng cho GraphQL (security/gql-throttler.guard.ts)
  • Tracing riêng cho resolver (graphql/tracing/resolver-tracing.plugin.ts) để giữ observability nhất quán với REST (xem ADR-0004)
  • Docs tự sinh dạng Swagger-style tại GET /graphql/docs (spectaql), chỉ bật non-production

Lựa chọn khác đã cân nhắc

  • Thay REST hoàn toàn bằng GraphQL: chi phí migrate toàn bộ client hiện tại quá lớn, rủi ro breaking change cao — loại.
  • BFF (Backend-for-Frontend) riêng biệt ngoài api-gateway: thêm 1 service mới cần vận hành, trong khi api-gateway đã có sẵn context auth/proxy tới mọi service — loại vì overhead không cần thiết ở quy mô hiện tại.
  • tRPC: phù hợp hơn khi FE/BE cùng ngôn ngữ và cùng monorepo type-sharing chặt, nhưng hệ sinh thái schema-first/tooling (playground, depth-limit, codegen cho nhiều loại client) của GraphQL phù hợp hơn với nhu cầu FE đa dạng — loại.

Hệ quả

  • Được: FE lấy đúng dữ liệu cần trong 1 request, giảm over/under-fetch; DataLoader giảm N+1 khi resolver gọi nhiều service; REST không bị ảnh hưởng (rollout an toàn, có thể rollback GraphQL riêng nếu cần).
  • Đánh đổi: thêm bề mặt tấn công mới cần rate-limit/depth-limit riêng; thêm một tầng resolver cần maintain song song REST controller cho cùng domain; team cần hiểu thêm GraphQL schema design + DataLoader caching semantics.

Liên kết

  • Docs liên quan: docs/guides/GRAPHQL_API_GUIDE_EN.md, docs/guides/GRAPHQL_API_GUIDE_VI.md, docs/guides/GRAPHQL_VERIFICATION_GUIDE.md
  • Commit: 9b83c3e feat: implement graphql api with data loader for efficient data fetching
  • ADR liên quan: ADR-0001, ADR-0005