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.
@keymarks 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
__resolveReferencefunction tells a subgraph how to load an entity by its key.
