Skip to content
intermediate Phase 13 · Federation and Federated Architecture

Federation Architecture Overview

Splitting a GraphQL API into subgraphs composed behind a single gateway.

55m
1 problems
Topic Progress 0%

Why One Graph Stops Working at Team Scale

Why One Graph Stops Working at Team Scale

A five-person team can keep one GraphQL schema coherent because somebody holds the whole graph in their head. At forty engineers across catalog, checkout, and accounts that same schema becomes a shared build artifact: every team reviews every schema change, every release ships every resolver, and one runaway query in catalog can starve checkout because both run in one process against one connection pool.

Federation removes the coupling without removing the single graph clients depend on. Each team runs a subgraph — an ordinary GraphQL server with its own schema, resolvers, datastore, and release cadence. A build step composes every subgraph schema into one supergraph, and a gateway serves that supergraph as the only endpoint clients talk to.

before                              after

+-------------------------+        client --- one endpoint, one schema
| one schema              |            |
| one resolver map        |            v
| one deploy, one DB      |     +--------------+
| 40 engineers commit     |     |    gateway   |  plans, then fans out
+-------------------------+     +--------------+
                                   |      |      |
                                 catalog checkout accounts
                                 subgraph subgraph subgraph
                                 (own DB) (own DB) (own DB)

Clients see no difference: one endpoint, one document, one type system, one error shape. Ownership changes on the inside. The catalog team edits catalog types and deploys catalog alone, and a slow checkout query cannot exhaust catalog's database because they are separate processes with separate resources.

Subgraphs do not import each other's resolvers, migrations, or tables. The only thing they share is type definitions — and only the subset needed to reference each other, which the next topic defines precisely. Federation buys that team autonomy at the price of a composition step in CI, a gateway you now have to operate, and at least one extra network hop per operation. Whether the trade is worth it depends on team topology more than on request volume; see stitching versus federation for the lighter alternative and the org shapes where it wins.

The Four Pieces and One Request Path

The Four Pieces and One Request Path

Four nouns carry the entire architecture:

Piece What it is Who operates it
Subgraph a standalone GraphQL server with its own schema and data one product team
Composition a build step that merges subgraph schemas or fails loudly CI
Supergraph the composed schema, including routing metadata produced artifact
Gateway validates, plans, caches, and fans out every operation platform team

Take a product page query that spans three subgraphs:

query ProductPage($id: ID!) {
  product(id: $id) {          # products subgraph
    name                      # products subgraph
    reviews {                 # reviews subgraph, via entity hop
      rating
      author {                # accounts subgraph, second hop
        displayName
      }
    }
  }
}

The gateway does not forward that document verbatim to anyone. It plans first:

client --query--> gateway
                    |-- fetch 1 --> products  { product(id: $id) { id name } }
                    |-- fetch 2 --> reviews   representations: [{Product, id}]   after 1
                    |-- fetch 3 --> accounts  representations: [{User, id}]      after 2
                    <-- merged response shaped exactly like the original query ---

Each fetch is a plain GraphQL operation against one subgraph. Subgraphs never call each other; only the gateway fans out, which keeps the cross-service traffic in one place where it can be planned, traced, cached, and rate limited. Fields the gateway already has — Product.id from fetch 1 — are shipped to reviews as an entity representation instead of being fetched again.

Two consequences matter in production. First, an operation touching three subgraphs costs at least three sequential round trips, so cross-subgraph depth shows up directly in latency. Second, because planning happens before execution, an operation whose shape the gateway has seen before is served from a plan cache rather than re-planned, and that cache hit rate is one of the first numbers worth watching.

Drawing Ownership Lines, and When Not to Federate

Drawing Ownership Lines, and When Not to Federate

Good boundaries follow two forces: who owns the data, and how often the thing changes. Boundaries drawn by entity count produce subgraphs that must deploy together anyway, which reintroduces the coupling federation was supposed to remove.

Rules that hold up in practice

  • One team owns one subgraph. Shared ownership of a subgraph is the old monolith with extra latency.
  • Every field has exactly one owning subgraph. Two owners means composition cannot decide who resolves it.
  • A type that appears in more than one subgraph needs a key so the gateway can hop between them.
  • Avoid cycles where A's entity requires B's, which requires A's. The plan degenerates into long sequential chains.
  • Two subgraphs that always ship in the same release should be one subgraph until they stop.

What federation costs you

  • The gateway becomes critical path infrastructure: it needs redundancy, a warmed plan cache, and its own dashboards.
  • A schema registry or at minimum a CI composition step becomes mandatory, because composition failures otherwise surface at deploy time.
  • Local development gets heavier — you now run several subgraphs plus a gateway to exercise a single page.
  • Debugging spans processes: a single client operation becomes several traces, so distributed tracing stops being optional.

When to skip it. One team, one or two services, one deploy unit: a modular monolith gives you the same code-level boundaries with none of the infrastructure, and you can always extract later. Federation earns its keep when multiple teams need to change their slice of the graph without negotiating a shared release. If you cannot name a second team that would benefit, you do not need federation yet — and you will pay the gateway tax for nothing.

Interviewers like to probe exactly this boundary. A strong answer names both sides: federation is an organizational scaling tool first and a performance tool never — it adds hops, so it makes individual operations slower while making teams faster.

Practice Problems

0 / 1 solved
Partition a Monolithic Schema into Federated Subgraphs
Schema Partitioning

You own the migration of a monolithic storefront schema into federation. Split the types below into three subgraphs (catalog, reviews, accounts) following team ownership, and mark every type referenced across a boundary with a key so the gateway can hop to it.

Example:

Input: type Query { product(id: ID!): Product reviewsFor(productId: ID!): [Review!]! user(id: ID!): User } type Product { id: ID! name: String! priceCents: Int! } type Review { id: ID! rating: Int! body: String! author: User! } type User { id: ID! displayName: String! email: String! }

Output: # catalog subgraph type Query { product(id: ID!): Product } type Product @key(fields: "id") { id: ID! name: String! priceCents: Int! } # reviews subgraph type Query { reviewsFor(productId: ID!): [Review!]! } type Product @key(fields: "id") { id: ID! reviews: [Review!]! } type Review { id: ID! rating: Int! body: String! author: User! } type User @key(fields: "id") { id: ID! } # accounts subgraph type Query { user(id: ID!): User } type User @key(fields: "id") { id: ID! displayName: String! email: String! }

Each root field stays with the team that owns its data. Product is defined in two subgraphs, so both declare @key(fields: "id"); reviews reaches Product by reference and adds its own field, accounts owns User and reviews only declares the key fields it needs to hand author references onward.

Optimal Solution — O(1) per boundary check time, O(T) for T types space

Group root fields and object fields by owning team, then promote every cross-boundary type to an entity by adding @key(fields: "id") in each subgraph that mentions it.

# catalog
type Query { product(id: ID!): Product }
type Product @key(fields: "id") { id: ID! name: String! priceCents: Int! }

# reviews
type Query { reviewsFor(productId: ID!): [Review!]! }
type Product @key(fields: "id") { id: ID! reviews: [Review!]! }
type Review { id: ID! rating: Int! body: String! author: User! }
type User @key(fields: "id") { id: ID! }

# accounts
type Query { user(id: ID!): User }
type User @key(fields: "id") { id: ID! displayName: String! email: String! }

Edge Cases:

  • A field needed by two teams (Product.name) lives in one subgraph; the other reaches it through the entity hop
  • email must stay in accounts only, or authorization on it becomes unenforceable in one place
  • Moving Review.author from a plain field to an entity reference changes no client query — only the plan

Quiz

1. Where do production clients send their GraphQL operations in a federated graph?

Question 1 options

2. When does composition run in a federation setup?

Question 2 options

3. A query selects fields from three subgraphs, and reviews are reached through an entity hop. How many subgraph fetches should you expect?

Question 3 options

4. Why not let clients call each team’s GraphQL server behind a load balancer instead of running a gateway?

Question 4 options

Flashcards

Question

Subgraph

Answer

A standalone GraphQL server owning one slice of the graph: own schema, resolvers, datastore, and deploy cadence.

Question

Supergraph

Answer

The composed schema served by the gateway: client-visible types plus join metadata recording which subgraph owns each field.

Question

Gateway (router)

Answer

The process that validates, plans, caches, and fans out operations to subgraphs, then merges partial results.

Question

Composition

Answer

Build-time merge of subgraph schemas into a supergraph; either succeeds deterministically or fails with errors naming subgraph, type, and field.

Question

Cost of federation

Answer

Extra network hops per operation, a gateway to operate, and CI composition — you trade per-request latency for team autonomy.

Revision Notes

Key Takeaways

  • 1. Clients keep one endpoint and one type system; only ownership and deploys split into subgraphs
  • 2. The gateway plans before it forwards, so fetch count follows field ownership, not field count
  • 3. Federation is an organizational scaling tool: it makes teams faster and individual operations slightly slower

Interview Tips

  • • Walk the request path out loud: client → gateway plan → sequential/parallel subgraph fetches → entity hops → merged response
  • • Answer "when would you not use federation" with a concrete org shape: one team, one deploy unit, modular monolith instead

Cheat Sheet

Pieces

  • Subgraph: standalone GraphQL server, one owning team
  • Composition: build-time merge in CI, output = supergraph
  • Supergraph: API schema + join routing metadata
  • Gateway: validate → plan → cache plan → fan out → merge

Request path

client → gateway → fetch products { id name }
                     → fetch reviews (representations: Product{id})
                       → fetch accounts (representations: User{id})
                   ← merge into the client's original shape

Ownership rules

  • One team per subgraph; one owner per field
  • Shared type → declare a @key so the gateway can hop
  • Subgraphs never call each other; only the gateway fans out
  • Two subgraphs that always deploy together should be one subgraph

Skip federation when: one team, ≤2 services, no shared release pain → modular monolith.

Continue Learning