Chuyển đến nội dung chính

Lesson 19: GraphQL Federation — Unified API for Micro Frontend

Apollo Federation: Supergraph, Subgraphs, Routers. Each microservice exposes the GraphQL subgraph. Router composes into a unified API. Schema stitching vs Federation. Performance considerations.

🏗️ Architecture — Lesson 19 Lesson 19: GraphQL Federation — Unified API for Micro Frontend

Microservices & Micro Frontend system design — From basics to Production

Part 6: API Gateway & BFF Layer

xdev.asia

Introduction

GraphQL Federation allows each microservice to expose a subgraph, and the Router automatically composes it into an unified supergraph. Frontend only needs 1 endpoint to query data from every service.

GraphQL Federation — unified graph from multiple subgraphs


1. Problem: Multiple GraphQL Endpoints

❌ Mỗi service có GraphQL endpoint riêng:
Frontend → product.api.com/graphql (Product schema)
Frontend → user.api.com/graphql (User schema)
Frontend → order.api.com/graphql (Order schema)

→ Frontend phải biết endpoint nào chứa data gì
→ Không thể query cross-service trong 1 request
→ Ví dụ: Order + Product + User = 3 requests

2. GraphQL Federation Architecture

✅ Unified Supergraph:

┌─────────────┐
│   Frontend  │
│  1 endpoint │
└──────┬──────┘
       │
       ▼
┌──────────────────────┐
│   Apollo Router      │
│   (Supergraph)       │
│   gateway.api.com    │
└──┬────────┬────────┬─┘
   │        │        │
   ▼        ▼        ▼
┌──────┐ ┌──────┐ ┌──────┐
│Product│ │ User │ │Order │
│Subgra│ │Subgra│ │Subgra│
│  ph  │ │  ph  │ │  ph  │
└──────┘ └──────┘ └──────┘

1 query → Router splits → Subgraphs → Router merges → 1 response

3. Subgraph Definition

3.1 Product Subgraph

# Product Service subgraph
type Product @key(fields: "id") {
  id: ID!
  name: String!
  price: Float!
  description: String
  category: Category!
}

type Category {
  id: ID!
  name: String!
}

type Query {
  product(id: ID!): Product
  products(limit: Int, offset: Int): [Product!]!
}

3.2 Review Subgraph (extends Product)

# Review Service subgraph — extends Product
type Product @key(fields: "id") {
  id: ID!
  reviews: [Review!]!
  averageRating: Float
}

type Review {
  id: ID!
  rating: Int!
  comment: String
  author: User!
}

type Query {
  reviews(productId: ID!): [Review!]!
}

3.3 Federated Query

# Frontend query — Router handles cross-service resolution
query ProductPage($id: ID!) {
  product(id: $id) {
    id              # → Product Subgraph
    name            # → Product Subgraph
    price           # → Product Subgraph
    reviews {       # → Review Subgraph
      rating
      comment
      author {      # → User Subgraph
        name
        avatar
      }
    }
    averageRating   # → Review Subgraph
  }
}

Automatic Routers:

  1. Query Product Subgraph → get product data
  2. Query Review Subgraph → get reviews (using product.id)
  3. Query User Subgraph → get author info (use review.author.id)
  4. Merge all → return 1 response

4. Schema Stitching vs Federation

Schema StitchingFederation
OwnershipGateway owns schemaServices own schema
CouplingHigh (gateway knows about services)Low (services declare themselves)
ScalabilityGateway bottleneckDistributed
EvolutionHard (central change)Easy (service-level)
Verdict❌ Legacy✅ Recommended

5. Performance Considerations

5.1 Query Plan Optimization

Router tạo query plan tối ưu:
─ Parallel: Product + User subgraphs (independent)
─ Sequential: Reviews → after Product (needs product.id)

5.2 Dataloader Pattern

// Trong Review Subgraph, batch user lookups
const userLoader = new DataLoader(async (userIds) => {
  const users = await userService.getUsers(userIds);
  return userIds.map(id => users.find(u => u.id === id));
});

// Resolve author field
Review: {
  author: (review) => userLoader.load(review.authorId)
}

5.3 Caching

Persisted Queries: Client gửi query hash thay vì full query
Automatic Persisted Queries (APQ): Router cache query plans
CDN caching: @cacheControl directive

6. When to use Federation?

✅ Dùng khi:
- Nhiều microservices cần unified GraphQL API
- Frontend teams muốn 1 endpoint
- Complex, nested data relationships
- Multiple frontend clients

❌ Không cần khi:
- Chỉ có 1-2 services (đơn giản quá)
- REST đã đủ tốt
- Team chưa quen GraphQL
- Performance-critical (thêm latency qua Router)

Summary

  • Federation = each service own subgraph, Router composes supergraph
  • @key directive for entity reference across subgraphs
  • Router automatically split, resolve, merge queries
  • DataLoader pattern avoids N+1 problem
  • Use when you need unified GraphQL API for multiple services

Next article: Lesson 20: Testing Microservices — Unit, Integration & E2E