Episode 22 builds distributed schemas with Apollo Federation 2: the supergraph and gateway concepts, the @key, @shareable, @override, and @interfaceObject directives, how to build subgraphs, setting up Apollo Gateway with composition and query planning, and migration strategies from a monolith.

As teams and domains grow, a single giant schema becomes a bottleneck: every change requires coordination from everyone. Episode 22 introduces Apollo Federation — an architecture that splits the schema into subgraphs owned by each team, then combines them into a single supergraph.
We'll learn the supergraph and gateway concepts, the key Federation 2 directives, how to build subgraphs, setting up Apollo Gateway with query planning, and a step-by-step migration strategy from a monolith.
In federated GraphQL, each domain (user, product, order) becomes a subgraph — a standalone GraphQL service with its own partial schema. All subgraphs are combined by a gateway into a supergraph, which clients see as a single endpoint.
client -> gateway -> subgraph users
-> subgraph products
-> subgraph ordersThe benefits: each team owns its own schema, deploys independently, and scales per domain. The challenge: composing schemas across services needs clear rules — that's where the federation directives come in.
Federation 2 (the latest stable version) simplifies composition with directives:
@key(fields: "id"): marks a unique field as the cross-subgraph entity reference.@shareable: a field that may be defined by multiple subgraphs.@override(from: "OtherSubgraph"): takes over a field's implementation from another subgraph.@interfaceObject: lets a subgraph contribute fields to an interface.type Product @key(fields: "upc") {
upc: String!
name: String!
price: Int!
}
type Query {
products: [Product!]!
}Another subgraph can extend the Product entity without owning its data:
type Product @key(fields: "upc") {
upc: String! @external
reviews: [Review!]! @requires(fields: "upc")
}
type Review {
id: ID!
body: String!
rating: Int!
}This pattern is the power of federation: the reviews service only knows upc, and the gateway gathers the complete data when a query needs both.
Each subgraph is a regular Apollo server, built with buildSubgraphSchema:
npm install @apollo/subgraphimport { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
import { buildSubgraphSchema } from "@apollo/subgraph";
const schema = buildSubgraphSchema({ typeDefs, resolvers });
const server = new ApolloServer({ schema });
const { url } = await startStandaloneServer(server, { listen: { port: 4002 } });
console.log("Subgraph products di", url);A subgraph is exposed to the gateway via its URL, along with its introspected schema. Every subgraph needs a __resolveReference resolver for entities that other subgraphs can reference:
const resolvers = {
Product: {
__resolveReference(ref, ctx) {
return ctx.db.products.findByUpc(ref.upc);
},
},
Query: {
products: (_, __, ctx) => ctx.db.products.findAll(),
},
};The gateway combines subgraphs. First install it via npm install @apollo/gateway @apollo/server, then define the subgraph list:
import { ApolloGateway } from "@apollo/gateway";
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
const gateway = new ApolloGateway({
supergraphSdl: `
schema @link(url: "https://specs.apollo.dev/federation/v2.3") {
query: Query
}
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key", "@shareable"])
`,
subgraphs: {
users: { url: "http://localhost:4001" },
products: { url: "http://localhost:4002" },
},
});
const server = new ApolloServer({ gateway });
const { url } = await startStandaloneServer(server, { listen: { port: 4000 } });The gateway does two important things: composition (combining subgraphs into a supergraph) and query planning (splitting one client query into calls to the right subgraphs). Clients only deal with the gateway — the supergraph endpoint.
A frequently used pattern: value types (simple objects without their own identity, which can be @shareable) versus entities (which have a @key). For monitoring, the gateway and each subgraph can send tracing to Apollo Studio, making the query journey across services visible.
A gradual migration so clients aren't disrupted:
This incremental approach is used by many companies — and we'll dissect it again in the microservices context in episode 36.
Key takeaways:
@key marks entity references; @external and @requires enable cross-subgraph extensions.@shareable and @override govern field ownership in Federation 2.buildSubgraphSchema and __resolveReference.In the next episode, episode 23, you'll learn about GraphQL Code Generator and type safety — installing and configuring codegen, generating TypeScript types from the schema, generating typed React hooks, type-safe resolvers and context, and integrating watch mode and pre-commit hooks. You'll experience end-to-end type safety!