[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
---
|
||||
id: backend-graphql-federation
|
||||
title: GraphQL Federation — Apollo / Mesh / Hive
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [backend, graphql, vibe-coding]
|
||||
tech_stack: { language: "TS", applicable_to: ["Backend"] }
|
||||
applied_in: []
|
||||
aliases: [GraphQL Federation, Apollo Federation, GraphQL Mesh, Hive, supergraph, subgraph]
|
||||
---
|
||||
|
||||
# GraphQL Federation
|
||||
|
||||
> Microservice 가 GraphQL = federation. **Subgraph (per service) → Supergraph (gateway)**. Apollo Federation 가 표준. 작은 system 가 stitching.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- 매 service 가 own schema (subgraph).
|
||||
- Gateway 가 query plan + 분산 실행.
|
||||
- Entity (cross-service type).
|
||||
- Schema composition.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Federation 식
|
||||
```graphql
|
||||
# users service (subgraph)
|
||||
type User @key(fields: "id") {
|
||||
id: ID!
|
||||
email: String!
|
||||
}
|
||||
|
||||
# orders service (subgraph)
|
||||
type Order {
|
||||
id: ID!
|
||||
total: Float!
|
||||
user: User! # users service 의 User
|
||||
}
|
||||
|
||||
extend type User @key(fields: "id") {
|
||||
id: ID! @external
|
||||
orders: [Order!]! # orders 가 추가
|
||||
}
|
||||
```
|
||||
|
||||
→ User 가 두 service 가 정의 (federated).
|
||||
|
||||
### Apollo Server (subgraph)
|
||||
```ts
|
||||
import { ApolloServer } from '@apollo/server';
|
||||
import { buildSubgraphSchema } from '@apollo/subgraph';
|
||||
|
||||
const typeDefs = `
|
||||
type User @key(fields: "id") {
|
||||
id: ID!
|
||||
email: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
users: [User!]!
|
||||
}
|
||||
`;
|
||||
|
||||
const resolvers = {
|
||||
Query: { users: () => db.users.find() },
|
||||
User: {
|
||||
__resolveReference: ({ id }) => db.users.findOne({ id }),
|
||||
},
|
||||
};
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema: buildSubgraphSchema({ typeDefs, resolvers }),
|
||||
});
|
||||
```
|
||||
|
||||
→ `__resolveReference` 가 다른 service 가 reference.
|
||||
|
||||
### Apollo Router (gateway)
|
||||
```yaml
|
||||
# router.yaml
|
||||
supergraph:
|
||||
listen: 0.0.0.0:4000
|
||||
|
||||
subgraphs:
|
||||
users:
|
||||
routing_url: http://users:4001
|
||||
orders:
|
||||
routing_url: http://orders:4002
|
||||
```
|
||||
|
||||
```bash
|
||||
router --config router.yaml
|
||||
```
|
||||
|
||||
→ Single endpoint, query 가 자동 분산.
|
||||
|
||||
### Supergraph composition
|
||||
```bash
|
||||
# Compose subgraph schemas → supergraph
|
||||
rover supergraph compose --config supergraph.yaml > supergraph.graphql
|
||||
```
|
||||
|
||||
```yaml
|
||||
# supergraph.yaml
|
||||
subgraphs:
|
||||
users:
|
||||
routing_url: http://users:4001
|
||||
schema:
|
||||
file: users/schema.graphql
|
||||
orders:
|
||||
routing_url: http://orders:4002
|
||||
schema:
|
||||
file: orders/schema.graphql
|
||||
```
|
||||
|
||||
### Apollo Studio / Hive
|
||||
```
|
||||
Schema registry:
|
||||
- 매 service 가 schema publish.
|
||||
- Compose 가 검증.
|
||||
- Breaking change 감지.
|
||||
- Performance 가 visualize.
|
||||
|
||||
→ Apollo Studio (paid).
|
||||
Hive (open source, free).
|
||||
```
|
||||
|
||||
### Entity resolution
|
||||
```graphql
|
||||
query {
|
||||
orders {
|
||||
id
|
||||
user {
|
||||
email # users service 가
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Gateway 가:
|
||||
1. orders service: orders + user.id 가져옴.
|
||||
2. users service: User(id: ...) batch.
|
||||
3. Merge.
|
||||
```
|
||||
|
||||
→ N+1 자동 batch (DataLoader).
|
||||
|
||||
### Federation directives
|
||||
```graphql
|
||||
@key(fields: "id") # Entity (cross-service)
|
||||
@external # Field 가 다른 service 가
|
||||
@requires(fields: "stock") # 다른 service field 필요
|
||||
@provides(fields: "name") # 이 service 가 줌
|
||||
@shareable # 여러 service 가 정의 OK
|
||||
@override(from: "old-service") # Migration
|
||||
@inaccessible # Public 안 보임
|
||||
```
|
||||
|
||||
### GraphQL Mesh
|
||||
```yaml
|
||||
# mesh.yaml
|
||||
sources:
|
||||
- name: users
|
||||
handler:
|
||||
openapi:
|
||||
source: https://users/openapi.json
|
||||
- name: orders
|
||||
handler:
|
||||
graphql:
|
||||
endpoint: https://orders/graphql
|
||||
```
|
||||
|
||||
→ 매 source (REST, GraphQL, gRPC) 가 unified GraphQL.
|
||||
|
||||
→ Apollo Federation 가 GraphQL only. Mesh 가 다양.
|
||||
|
||||
### Schema stitching (legacy)
|
||||
```ts
|
||||
import { stitchSchemas } from '@graphql-tools/stitch';
|
||||
|
||||
const schema = stitchSchemas({
|
||||
subschemas: [usersSchema, ordersSchema],
|
||||
});
|
||||
```
|
||||
|
||||
→ Federation 의 옛 version. 작은 use case.
|
||||
|
||||
### Performance: query plan
|
||||
```graphql
|
||||
query {
|
||||
user(id: 1) {
|
||||
name
|
||||
orders {
|
||||
total
|
||||
product {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Plan:
|
||||
1. users service: user(id: 1) → {id, name}.
|
||||
2. orders service: orders for user.id 1 → [{id, total, productId}].
|
||||
3. products service: products([1, 2, 3]) batch → [{id, name}].
|
||||
|
||||
→ 3 service 호출, parallel where possible.
|
||||
```
|
||||
|
||||
### N+1 방지 (DataLoader)
|
||||
```ts
|
||||
import DataLoader from 'dataloader';
|
||||
|
||||
const userLoader = new DataLoader(async (ids: string[]) => {
|
||||
const users = await db.users.find({ id: { $in: ids } });
|
||||
return ids.map(id => users.find(u => u.id === id));
|
||||
});
|
||||
|
||||
// Resolver
|
||||
User: { id: ({ id }) => userLoader.load(id) }
|
||||
```
|
||||
|
||||
→ 매 request 가 자체 loader. Batch.
|
||||
|
||||
### Authentication
|
||||
```ts
|
||||
// Gateway 가 auth header forward
|
||||
const router = new Router({
|
||||
subgraphs: {
|
||||
users: { routing_url: '...', willSendRequest: ({ request }) => {
|
||||
request.http.headers.set('user-id', context.userId);
|
||||
}},
|
||||
},
|
||||
});
|
||||
|
||||
// Subgraph 가 user-id header 사용
|
||||
```
|
||||
|
||||
### Rate limit
|
||||
```
|
||||
Gateway level (Apollo Router):
|
||||
- Query depth limit (5).
|
||||
- Query complexity limit (1000 cost).
|
||||
- Per-user rate.
|
||||
|
||||
Subgraph level:
|
||||
- Per-resolver rate.
|
||||
```
|
||||
|
||||
### Caching
|
||||
```
|
||||
Apollo Router:
|
||||
- Response cache.
|
||||
- Per-field hint (@cacheControl).
|
||||
- CDN cache.
|
||||
|
||||
Subgraph:
|
||||
- DataLoader (per-request).
|
||||
- Redis (per-field).
|
||||
```
|
||||
|
||||
```graphql
|
||||
type User @cacheControl(maxAge: 300) {
|
||||
id: ID!
|
||||
email: String! @cacheControl(maxAge: 60)
|
||||
}
|
||||
```
|
||||
|
||||
### Tracing
|
||||
```
|
||||
- Apollo Studio: query timing.
|
||||
- OpenTelemetry: span 별 service.
|
||||
- Prometheus: metric.
|
||||
|
||||
→ "이 query 가 어디 서비스 가 느림?" visible.
|
||||
```
|
||||
|
||||
### Schema linting
|
||||
```bash
|
||||
# Apollo Rover
|
||||
rover graph check my-graph@current --schema schema.graphql
|
||||
```
|
||||
|
||||
→ Breaking change 감지.
|
||||
|
||||
### Versioning
|
||||
```
|
||||
GraphQL = no version (single schema 가 evolve).
|
||||
- Add fields: OK.
|
||||
- Remove field: deprecated → grace period → remove.
|
||||
- Rename: 새 field + 옛 deprecated.
|
||||
|
||||
→ Mobile app 가 옛 client 도 작동.
|
||||
```
|
||||
|
||||
### Federation 의 alternative
|
||||
```
|
||||
- BFF (Backend for Frontend): per-client REST + GraphQL.
|
||||
- API Gateway (REST): 단순.
|
||||
- gRPC + Buf: type-safe.
|
||||
- tRPC: TS-only.
|
||||
|
||||
→ GraphQL Federation 가 큰 system + 다양 client.
|
||||
```
|
||||
|
||||
### When NOT?
|
||||
```
|
||||
- 작은 system (1-2 service): 직접 GraphQL.
|
||||
- Single client: REST 충분.
|
||||
- 매우 high-throughput: gRPC.
|
||||
- Mobile-only + simple: tRPC / REST.
|
||||
```
|
||||
|
||||
### Migration
|
||||
```
|
||||
1. 1 monolith GraphQL.
|
||||
2. 1 service split → subgraph.
|
||||
3. Gateway 추가 (federation 활성).
|
||||
4. 점진 split.
|
||||
5. 옛 monolith retire.
|
||||
```
|
||||
|
||||
→ Strangler fig.
|
||||
|
||||
### Production
|
||||
```
|
||||
- Apollo Router (Rust, 빠름).
|
||||
- Hive (open source registry).
|
||||
- Mesh (REST → GraphQL).
|
||||
- ko / pothos / Yoga / Mercurius (subgraph servers).
|
||||
```
|
||||
|
||||
### Cost / overhead
|
||||
```
|
||||
Federation 가 큰 cost:
|
||||
- Gateway latency (10-50ms).
|
||||
- Schema composition complexity.
|
||||
- 매 service 가 federation aware.
|
||||
|
||||
→ 5+ service GraphQL 가 sweet spot.
|
||||
```
|
||||
|
||||
### Real-world
|
||||
- **Netflix**: 큰 GraphQL Federation.
|
||||
- **Airbnb**: federation 사용.
|
||||
- **Expedia**: federation.
|
||||
- **Walmart**: 큰 multi-team.
|
||||
- **Atlassian**: federation 마이그레이션.
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 상황 | 추천 |
|
||||
|---|---|
|
||||
| 단일 GraphQL | 직접 server |
|
||||
| 5+ subgraph | Apollo Federation |
|
||||
| Mixed source | GraphQL Mesh |
|
||||
| 작은 (legacy) | Schema stitching |
|
||||
| Schema registry | Apollo Studio / Hive |
|
||||
| 빠른 gateway | Apollo Router (Rust) |
|
||||
| Simple TS RPC | tRPC |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **모든 거 federation (작은 system)**: 큰 overhead.
|
||||
- **No DataLoader**: N+1 폭발.
|
||||
- **Schema 무계획 변경**: client 깨짐.
|
||||
- **No cache**: latency.
|
||||
- **Auth gateway 만**: subgraph 도 검증.
|
||||
- **No tracing**: bottleneck 모름.
|
||||
- **Subgraph 가 cross-service join**: federation 의 가치 ↓.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Federation = supergraph + subgraph + entity.
|
||||
- Apollo Router 가 빠름 (Rust).
|
||||
- Hive 가 open source registry.
|
||||
- DataLoader 가 N+1 의 답.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Backend_GraphQL_Server_Patterns]]
|
||||
- [[Backend_GraphQL_Yoga_Pothos]]
|
||||
- [[Backend_API_Gateway_BFF]]
|
||||
Reference in New Issue
Block a user