Why Compose Multiple GraphQL Services?
As an organization grows, a single monolithic GraphQL server owned by one team becomes a bottleneck — every schema change requires coordinating across every team that owns a piece of the graph, and deploys get coupled together. Schema stitching and federation both solve this by letting multiple independently deployed GraphQL services, each owned by a different team, be composed into one unified graph that clients query as if it were a single API, without clients ever knowing the data actually comes from several backend services.
Cricket analogy: A monolithic graph owned by one team is like a single curator responsible for every stat on a cricket broadcast; composing services is like letting the batting-stats team, bowling-stats team, and fielding-stats team each own their data feed while the broadcast still looks like one seamless screen.
Schema Stitching
Schema stitching, as implemented by tools like GraphQL Tools' stitchSchemas, works by merging the type definitions and resolvers of multiple existing GraphQL schemas at a gateway layer, using type merging configuration to tell the gateway how to resolve fields that span services — for example, fetching a User from an accounts service and then delegating to a separate orders service to resolve that user's orders field, stitching the two responses together into one. Because stitching operates over whole schemas that were often designed independently, the gateway needs explicit configuration describing how types relate across services, and changes to one underlying schema can require updates to the gateway's stitching configuration.
Cricket analogy: Schema stitching is like a broadcast director manually cutting between a batting-stats camera feed and a separate bowling-stats camera feed, requiring an explicit run sheet describing exactly when and how to switch between the two independently operated feeds.
// Simplified schema stitching example (graphql-tools)
const gatewaySchema = stitchSchemas({
subschemas: [
{ schema: accountsSchema, executor: accountsExecutor },
{ schema: ordersSchema, executor: ordersExecutor },
],
typeDefs: `
extend type User {
orders: [Order!]!
}
`,
resolvers: {
User: {
orders: {
selectionSet: '{ id }',
resolve(user, args, context, info) {
return delegateToSchema({
schema: ordersSchema,
operation: 'query',
fieldName: 'ordersByUserId',
args: { userId: user.id },
context, info,
});
},
},
},
},
});Apollo Federation
Apollo Federation takes a schema-first, service-owned approach: each subgraph declares which types and fields it owns using directives like @key (marking a type's unique identifier for cross-service resolution), @external, @requires, and @provides, and a gateway (or the newer, self-hosted-friendly router) composes those subgraph schemas into one supergraph at build time, then plans and executes a query plan that fans out to the relevant subgraphs at runtime. The key difference from stitching is that federation pushes the composition rules into each subgraph's own schema — the accounts service declares type User @key(fields: "id"), and the orders service independently extends that same type with extend type User @key(fields: "id") { orders: [Order!]! } — so composition is driven by declarative ownership rather than centralized gateway configuration.
Cricket analogy: Federation is like each franchise in the IPL independently declaring its own player registry with a shared unique player ID standard, so a central league system can automatically compose everyone's data without a human manually mapping IDs across teams.
# accounts subgraph
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
# orders subgraph
type Order {
id: ID!
total: Float!
userId: ID!
}
extend type User @key(fields: "id") {
id: ID! @external
orders: [Order!]!
}In Apollo Federation, the @key directive is what makes a type an 'entity' — a type whose fields can be contributed by more than one subgraph. The gateway's query planner uses @key fields to know how to fetch a base representation from one subgraph and then request additional fields for that same entity from another.
Stitching vs Federation: Choosing an Approach
Schema stitching is generally easier to retrofit onto existing, independently designed schemas since it doesn't require each service to adopt federation-specific directives, but it concentrates composition logic and cross-service knowledge in the gateway, which becomes a coordination point again as the number of services grows. Federation front-loads more setup — every subgraph must be authored with federation directives from the start — but it distributes ownership of composition rules to each subgraph team and scales better organizationally for large numbers of independently owned services, which is why it has become the dominant pattern for large-scale multi-team GraphQL platforms.
Cricket analogy: Choosing stitching over federation is like a tournament committee manually reconciling stats from teams using different scoring software, versus mandating upfront that every team's software must export in one standardized format from day one — more setup, but it scales to more teams cleanly.
Federation's @key fields must resolve consistently and cheaply, since the gateway calls back into subgraphs to 'extend' entities using that key on nearly every cross-service query. Choosing a key that requires an expensive lookup (like a full-text search) rather than a simple indexed ID can turn every federated query touching that type into a performance bottleneck.
- Composing multiple GraphQL services solves the coordination bottleneck of a single team owning one monolithic schema.
- Schema stitching merges independently designed schemas at a centralized gateway using explicit type-merging configuration.
- Apollo Federation uses subgraph-declared directives (@key, @external, @requires, @provides) to distribute composition ownership.
- In federation, @key marks a type as an entity whose fields multiple subgraphs can contribute to.
- Stitching is easier to retrofit onto existing schemas; federation requires more upfront setup but scales better organizationally.
- Federation has become the dominant pattern for large-scale, multi-team GraphQL platforms.
- Entity key fields should be cheap and indexed, since the gateway resolves them on nearly every cross-service query.
Practice what you learned
1. What is the primary organizational problem that both schema stitching and federation solve?
2. In Apollo Federation, what does the @key directive do?
3. How does schema stitching differ from federation in terms of where composition logic lives?
4. Why is it risky to use an expensive, non-indexed field as a federation entity's @key?
5. Which of these is generally true when comparing the two approaches at scale?
Was this page helpful?
You May Also Like
Directives in GraphQL
Understand built-in and custom GraphQL directives, how they alter query execution and schema behavior, and where to apply them safely.
Authentication and Authorization in GraphQL
Learn how to authenticate requests and enforce field-level authorization in a GraphQL API, and avoid common security pitfalls.
Pagination Patterns: Cursor vs Offset
Compare offset-based and cursor-based pagination in GraphQL, and understand why the Relay connection spec is the industry-standard approach.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics