GraphQL Federation: Unifying Microservices into One Graph
GraphQL Federation (Apollo Federation 2) allows different teams to own different parts of a GraphQL schema. Each service publishes its subgraph, a Router combines them into one federated graph. Clients see one entry point, one query language—unaware of internal microservice structure.
Architecture
Client → Router (Apollo Router) → Users Subgraph
→ Products Subgraph
→ Orders Subgraph
→ Reviews Subgraph
Router receives GraphQL queries, builds execution plan, and calls needed subgraphs in parallel or sequence, merging results.
Subgraph: Users Service
# users-service/schema.graphql
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key", "@shareable"])
type Query {
me: User
user(id: ID!): User
}
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
createdAt: String!
}
// users-service/server.js
import { ApolloServer } from '@apollo/server'
import { buildSubgraphSchema } from '@apollo/subgraph'
const typeDefs = gql`...` // schema above
const resolvers = {
Query: {
me: (parent, args, context) => context.db.users.findById(context.userId),
user: (parent, { id }, context) => context.db.users.findById(id)
},
User: {
// Reference resolver: Router requests User by id from other service
__resolveReference: async ({ id }, context) => {
return context.db.users.findById(id)
}
}
}
const server = new ApolloServer({
schema: buildSubgraphSchema({ typeDefs, resolvers })
})
Subgraph: Products Service
# products-service/schema.graphql
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key", "@external", "@requires", "@provides"])
type Query {
products(categoryId: ID, limit: Int): [Product!]!
product(id: ID!): Product
}
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
stock: Int!
categoryId: ID!
}
# Extend User type from users-service
type User @key(fields: "id") {
id: ID!
# Add wishlist field to User owned by users-service
wishlist: [Product!]!
}
Composition and Deployment
# supergraph.yaml
federation_version: =2.3
subgraphs:
users:
routing_url: http://users-service:4000
products:
routing_url: http://products-service:4000
orders:
routing_url: http://orders-service:4000
# Generate composed schema
rover supergraph compose --config supergraph.yaml > supergraph.graphql
# Start router
docker run -p 4000:4000 \
-v ./supergraph.graphql:/etc/apollo/supergraph.graphql \
ghcr.io/apollographql/router:latest
Benefits
- Team autonomy: each team owns their subgraph
- Incremental adoption: migrate services one by one
- Shared types: reference types across subgraphs
- Single endpoint: clients unaware of microservices
Timelines
Basic federation setup (3 subgraphs, Router): 2–3 days. Complex multi-team setup with custom directives, advanced composition: 1–2 weeks.







