GraphQL Federation
ActiveGraphQL Federation is an architecture for composing multiple independent GraphQL services (subgraphs) into a single unified GraphQL API (supergraph). Each team owns and deploys their subgraph independently. A composition step merges all subgraph schemas into a supergraph schema. A router (gateway) receives client queries, plans which subgraphs to query, and assembles the response. Federation 2 (Apollo) is the most widely deployed implementation, also supported by WunderGraph Cosmo, The Guild / Hive, and Netflix DGS.
In one line
GraphQL Federation composes multiple GraphQL services (subgraphs) into one unified API (supergraph). Each subgraph is independently deployed and owns its schema slice. Entities span subgraphs via @key directives (primary keys). The router generates a query plan that fans out to relevant subgraphs and merges responses. Federation 2 directives: @key (entity key), @shareable (multiple subgraphs resolve same field), @external (field defined in another subgraph), @requires (computed from external field), @provides (optimistic resolution hint).
Quick Reference
| Field | Size | Description |
|---|---|---|
| @key | Entity primary key | @key(fields: "id") marks a type as an entity with a stable identifier. Entities can be referenced and resolved across subgraphs. Multiple @key directives define alternate keys. Compound keys: @key(fields: "userId orderId"). |
| @shareable | Multi-subgraph ownership | @shareable on a type or field allows multiple subgraphs to resolve it. Both subgraphs must return the same value. Used for value types that multiple teams need to extend without entity key overhead. |
| @external | Reference to other subgraph field | @external marks a field that is defined in another subgraph but needed locally for @requires or @provides. The local subgraph does not resolve @external fields – they come from the owning subgraph. |
| @requires | Computed from external fields | @requires(fields: "weight dimensions { height }") declares that this resolver needs specific fields from another subgraph before it can compute its value. The router fetches the required fields first, then calls this resolver. |
| @provides | Optimization hint | @provides(fields: "name") hints that this resolver can return the specified fields of a related entity without an additional subgraph fetch. Allows the router to optimize query plans by avoiding unnecessary federation joins. |
| @override | Field migration | @override(from: "SubgraphA") migrates a field from SubgraphA to the current subgraph. Enables gradual field ownership migration across teams without coordination. Progressive migration via the percent argument (@override(from: "A", label: "percent(20)")). |
| @inaccessible | Hide from supergraph | @inaccessible marks a type or field as present in subgraph schemas but excluded from the public supergraph. Used for internal fields that coordinate federation but should not be queryable by clients. |
| Composition | Build-time schema merge | The composition step merges all subgraph schemas into a supergraph SDL. Composition validates that entity keys are consistent, @requires fields exist, and no conflicts exist. Fails early: a schema bug is a composition error before any traffic is affected. |
| Query planning | Router generates plan | The router (Apollo Router, WunderGraph Cosmo Router) generates a query plan for each incoming query: which subgraphs to call, in what order, with what variables. Plans are cached. Complex queries generate multi-step plans with parallel and sequential fetch steps. |
Key Characteristics
Independent team ownership
Each team deploys their subgraph independently. Schema changes go through a schema registry that validates composition compatibility before deployment. Breaking changes are caught at compose time, not at runtime.
Entity resolution across subgraphs
The router resolves entities by sending a _entities query with representation objects (containing @key fields) to the owning subgraph. The subgraph's __resolveReference function maps keys to full objects.
Schema registry governance
Production federated graphs use a schema registry (Apollo GraphOS, WunderGraph Cosmo, The Guild Hive) to validate composition, track schema changes, detect breaking changes, and enforce approval workflows.
N+1 in federation
Naive entity resolution can trigger N _entities queries for a list of N items. The DataLoader pattern at the subgraph level and Apollo Router's entity batching (sending all keys in one _entities call) are essential for production performance.
Message Format
# Subgraph 1: Products service
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
}
# Subgraph 2: Reviews service – references Product entity
extend type Product @key(fields: "id") {
id: ID! @external
reviews: [Review!]!
}
type Review {
id: ID!
rating: Int!
text: String!
product: Product!
}
# Client query to supergraph router:
query ProductWithReviews($id: ID!) {
product(id: $id) {
name # resolved by Products subgraph
price # resolved by Products subgraph
reviews { # resolved by Reviews subgraph
rating
text
}
}
}# Router query plan (simplified):
# 1. Fetch {product(id: $id) { name price __typename id }} from Products
# 2. Fetch {_entities(representations: [{__typename: "Product", id: $id}]) {
# ... on Product { reviews { rating text } }
# }} from Reviews
# 3. Merge and return unified response
# _entities query (Federation's internal mechanism)
query EntityResolution($representations: [_Any!]!) {
_entities(representations: $representations) {
... on Product {
reviews {
rating
text
}
}
}
}
# Variables:
{ "representations": [{ "__typename": "Product", "id": "prod_123" }] }
# __resolveReference (Apollo Server subgraph):
Product: {
__resolveReference(ref: { id: string }, context) {
return context.dataSources.reviews.getByProductId(ref.id);
}
}