正在加载,请稍候…

GraphQL Federation: Unified Schema Across Microservices

Implement GraphQL Federation with Apollo Federation v2. Learn subgraphs, entity resolution, directives, schema composition, and building a distributed GraphQL architecture.

GraphQL Federation: Unified Schema Across Microservices

What is Federation?

Without Federation:
  Client -> Each service has its own GraphQL endpoint
  Client -> /api/users/graphql
  Client -> /api/orders/graphql
  Client -> /api/products/graphql

With Federation:
  Client -> Single Gateway -> Routes to appropriate subgraphs
  Unified schema: user has orders has products

Subgraph Setup

// users-service/schema.ts
import { buildSubgraphSchema } from '@apollo/subgraph';
import { gql } from 'graphql-tag';

const typeDefs = gql`
  extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])

  type User @key(fields: "id") {
    id: ID!
    email: String!
    name: String!
    profile: UserProfile
  }

  type UserProfile {
    bio: String
    avatarUrl: String
  }

  type Query {
    me: User
    user(id: ID!): User
  }
`;

const resolvers = {
  Query: {
    me: (_, __, { userId }) => userRepo.findById(userId),
    user: (_, { id }) => userRepo.findById(id),
  },
  User: {
    // Reference resolver: allows other subgraphs to extend User
    __resolveReference(reference: { id: string }) {
      return userRepo.findById(reference.id);
    },
    profile: (user) => userProfileRepo.findByUserId(user.id),
  },
};

export const schema = buildSubgraphSchema({ typeDefs, resolvers });
// orders-service/schema.ts
const typeDefs = gql`
  extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@extends"])

  # Extend User type from users-service
  type User @key(fields: "id") {
    id: ID! @external
    orders(first: Int): OrderConnection!
  }

  type Order @key(fields: "id") {
    id: ID!
    status: OrderStatus!
    total: Float!
    createdAt: String!
    items: [OrderItem!]!
  }

  type Query {
    order(id: ID!): Order
  }
`;

const resolvers = {
  User: {
    orders: (user, { first = 10 }) => orderRepo.findByUserId(user.id, { limit: first }),
  },
  Order: {
    __resolveReference(ref: { id: string }) {
      return orderRepo.findById(ref.id);
    },
  },
};

Apollo Router (Gateway)

# router.yaml
supergraph:
  listen: 0.0.0.0:4000

subgraphs:
  users:
    routing_url: http://users-service:4001/graphql
  orders:
    routing_url: http://orders-service:4002/graphql
  products:
    routing_url: http://products-service:4003/graphql
# Compose supergraph schema
rover supergraph compose --config supergraph.yaml > supergraph.graphql

# Start router
./router --config router.yaml --supergraph supergraph.graphql

Schema Composition

# Resulting unified schema (auto-composed by Apollo Router)
type User {
  id: ID!
  email: String!
  name: String!
  profile: UserProfile      # From users-service
  orders(first: Int): OrderConnection!  # From orders-service
  recommendations: [Product!]!  # From products-service
}

Client Query (spans multiple services)

query GetUserDashboard($userId: ID!) {
  user(id: $userId) {
    name
    email
    # Resolved by users-service
    profile {
      avatarUrl
    }
    # Resolved by orders-service
    orders(first: 5) {
      edges {
        node {
          id
          status
          total
        }
      }
    }
  }
}
# Apollo Router automatically:
# 1. Fetches user from users-service
# 2. Fetches orders using user.id from orders-service
# 3. Merges and returns combined result

Schema Registry

# Publish subgraph schemas to Apollo Studio
rover subgraph publish my-graph@prod   --name users   --schema users-service/schema.graphql   --routing-url https://users-service.internal/graphql

# Check for breaking changes before publishing
rover subgraph check my-graph@prod   --name users   --schema users-service/schema.graphql

Federation enables teams to own their GraphQL schemas independently while presenting a unified API.