GraphQL Federation – Splitting Your Schema

Apollo Federation lets you split one large GraphQL schema across many small services, each owned by a different team. A central router combines them into one API that clients query as if it were a single server. Each service is fully independent — it can be deployed, scaled, and changed without coordinating with other teams.

The Monolith Problem Federation Solves

  Monolith GraphQL server — one team changes it, all teams wait:
  ──────────────────────────────────────────────────────────────
  ┌───────────────────────────────────────────────┐
  │  Single GraphQL Server                        │
  │  Users + Products + Orders + Reviews + Search │
  │  All deployed together                        │
  └───────────────────────────────────────────────┘

  Federation — each team owns its service:
  ─────────────────────────────────────────
  Router (public)
    │
    ├── Users Subgraph     (Team A — deploys independently)
    ├── Products Subgraph  (Team B — deploys independently)
    ├── Orders Subgraph    (Team C — deploys independently)
    └── Reviews Subgraph   (Team D — deploys independently)

Federation Key Concepts

  Concept        Meaning
  ───────        ───────
  Subgraph       A standalone GraphQL service that publishes
                 part of the overall schema

  Router         The public entry point that combines all
                 subgraphs into one unified API

  @key           Marks the field(s) that uniquely identify
                 an entity across subgraphs (like a primary key)

  Entity         A type that multiple subgraphs can contribute
                 fields to, identified by @key

  Reference      A way one subgraph "borrows" an entity
  resolver       defined in another subgraph to add fields to it

Defining an Entity with @key

  Users Subgraph — owns the User type:
  ─────────────────────────────────────
  type User @key(fields: "id") {
    id:    ID!
    name:  String!
    email: String!
  }

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

  Orders Subgraph — extends User to add orders:
  ──────────────────────────────────────────────
  type User @key(fields: "id") {
    id:     ID! @external       ← Defined in Users Subgraph
    orders: [Order!]!           ← Added by Orders Subgraph
  }

  type Order {
    id:    ID!
    total: Float!
  }

  # Reference resolver — tells Orders Subgraph how to
  # load a User by its id when the router needs to
  extend type Query {
    _entities(representations: [_Any!]!): [_Entity]!
  }

How the Router Resolves a Cross-Subgraph Query

  Client query:
  ──────────────
  {
    user(id: "u5") {
      name          ← in Users Subgraph
      orders {      ← in Orders Subgraph
        total
      }
    }
  }

  Router steps:
  ──────────────
  1. Router asks Users Subgraph: user(id:"u5") { name id }
     → Returns { id:"u5", name:"Ravi" }

  2. Router asks Orders Subgraph: user entity with id:"u5" { orders { total } }
     → Returns { orders: [{ total: 450 }] }

  3. Router merges both results and sends one response:
     { user: { name:"Ravi", orders: [{ total: 450 }] } }

Setting Up a Subgraph

  npm install @apollo/subgraph

  import { buildSubgraphSchema } from '@apollo/subgraph';

  const typeDefs = gql`
    type Product @key(fields: "id") {
      id:    ID!
      name:  String!
      price: Float!
    }

    type Query {
      product(id: ID!): Product
      allProducts: [Product!]!
    }
  `;

  const resolvers = {
    Product: {
      __resolveReference(ref) {
        // ref = { id: "p1" } — load Product by id
        return db.products.findById(ref.id);
      }
    },
    Query: { ... }
  };

  const server = new ApolloServer({
    schema: buildSubgraphSchema({ typeDefs, resolvers })
  });

Key Points

  • Federation splits a large schema across independent subgraph services, each owned by a separate team.
  • The router combines all subgraphs into one public API automatically.
  • @key marks the unique identifier of an entity so the router can stitch data across subgraphs.
  • Each subgraph can be deployed, scaled, and updated without touching other subgraphs.
  • The __resolveReference function tells a subgraph how to load an entity by its key.

Leave a Comment