GraphQL Federation

GraphQL Federation is an architectural approach for combining multiple independent GraphQL services into one unified API. Each service contributes part of the overall schema, while a GraphQL gateway executes queries across those services and returns a single response to clients.

Federation allows organizations to distribute ownership of their GraphQL API across different teams and services. Each team can develop, deploy, and scale its services independently while contributing to one coherent graph.

This approach is particularly valuable for larger applications, where different teams own different parts of the business domain but clients still need a unified way to access their data.

What is GraphQL Federation?

GraphQL Federation divides a GraphQL API into independently developed services called subgraphs.

Each subgraph publishes a source schema describing the types and fields it provides. A process called schema composition validates these source schemas and combines them into one composite schema.

The composite schema defines the API clients see and query. Behind it, a GraphQL gateway coordinates requests across the subgraphs and assembles their results into a single response.

This architecture consists of four main concepts:

  • Subgraphs: Independent GraphQL services that contribute types and fields to the overall graph.
  • Source schemas: The GraphQL schemas published by individual subgraphs, including information about how they participate in federation.
  • Schema composition: The process of validating and combining source schemas into one composite schema.
  • Gateway: The entry point that executes client queries across the necessary subgraphs.

To clients, the entire system behaves like one GraphQL API, regardless of how many services participate behind it.

Why use GraphQL Federation?

Many GraphQL APIs start with a single server and schema. This is often the simplest architecture, especially when one team owns the entire API.

As applications grow, multiple teams may become responsible for different parts of the same graph. Maintaining everything within one application can introduce coordination overhead, particularly when teams need independent release schedules or different technology stacks.

Federation allows teams to preserve clear ownership boundaries without exposing the underlying service architecture to clients.

Independent development and deployment

Each team can own its subgraph, including its schema, implementation, and deployment lifecycle.

Teams can develop and release changes independently while schema composition validates how those changes interact with the rest of the graph.

Clear domain boundaries

GraphQL Federation aligns naturally with Domain-Driven Design (DDD), where complex applications are organized around distinct business domains and bounded contexts.

Subgraphs can represent these boundaries, making responsibilities explicit while allowing related data to be connected across services.

One unified GraphQL API

Clients interact with one GraphQL schema instead of integrating separately with every backend service.

Relationships between types can span subgraphs, allowing a single query to retrieve connected data from multiple services.

Independent scaling and technology choices

Subgraphs can use different programming languages, frameworks, and infrastructure.

Services can also be scaled independently according to their workloads, without requiring the entire GraphQL API to scale as one application.

How does GraphQL Federation work?

Consider an application where one service manages product information and another manages inventory.

The catalog service owns product details such as names and descriptions. The inventory service owns stock availability.

With federation, both services can contribute fields to the same Product type.

Defining subgraphs

The catalog subgraph exposes products and provides a way to retrieve them by ID.

# Catalog subgraph
 
type Query {
  productById(id: ID!): Product @lookup
}
 
type Product @key(fields: "id") {
  id: ID!
  name: String!
  description: String
}

The @key directive identifies fields that uniquely identify a product, while @lookup marks a query field that can retrieve that entity.

The inventory subgraph contributes the inStock field to the same product type.

# Inventory subgraph
 
type Query {
  productById(id: ID!): Product @lookup @internal
}
 
type Product @key(fields: "id") {
  id: ID!
  inStock: Boolean!
}

Both services describe the same entity through its stable key, but each owns different fields.

The inventory lookup provides a way for the distributed executor to retrieve inventory information for a particular product. Because the lookup is marked @internal, it is available for federation execution without becoming part of the public composite schema.

Schema composition

Schema composition validates the source schemas and determines how their types and fields fit together.

For this example, composition produces a client-facing schema similar to:

type Query {
  productById(id: ID!): Product
}
 
type Product {
  id: ID!
  name: String!
  description: String
  inStock: Boolean!
}

The resulting schema combines fields from both services into one coherent product model.

Composition also detects conflicts and incompatible schema definitions, allowing problems to be identified before a new composite schema is deployed.

Distributed query execution

A client can now query product information and inventory availability through one request.

query {
  productById(id: "123") {
    name
    inStock
  }
}

The gateway determines which subgraphs are needed to resolve the requested fields.

It retrieves the product’s name and identifying key from the catalog service, then uses the product key to fetch availability from the inventory service.

Finally, it assembles the results into one response:

{
  "data": {
    "productById": {
      "name": "GraphQL Handbook",
      "inStock": true
    }
  }
}

The client doesn’t need to know that two services contributed to the response. It simply queries the composite schema.

Entities, keys, and lookups

One of the defining capabilities of GraphQL Federation is connecting types across service boundaries.

An entity is a type with a stable identity that can be referenced across subgraphs.

A key, declared using @key, identifies an entity. A lookup, declared using @lookup, describes how that entity can be retrieved.

For example:

type Query {
  productById(id: ID!): Product @lookup
  productBySku(sku: String!): Product @lookup
}
 
type Product @key(fields: "id") @key(fields: "sku") {
  id: ID!
  sku: String!
  name: String!
}

A product can have multiple keys and multiple ways to retrieve it.

By expressing lookups as ordinary GraphQL fields, the specification allows services to participate in federation without implementing a specialized entity-resolution protocol.

The same principle applies to dependencies between fields. With the @require directive, subgraphs can declare the data they need from other subgraphs through ordinary GraphQL field arguments.

Together, keys, lookups, and requirements allow a distributed executor to navigate relationships across independently developed services.

From Apollo Federation to an open specification

Apollo introduced Apollo Federation in 2019, establishing many of the architectural concepts that define federated GraphQL APIs today.

GraphQL Fusion subsequently introduced a different approach to federation, built entirely on standard GraphQL capabilities. Rather than relying on hidden fields, specialized reference resolvers, or federation-specific protocols, Fusion proposed an architecture that uses the GraphQL type system and ordinary queries to connect services into a unified graph.

Both approaches demonstrated the value of federation, but differences in their schemas and execution protocols limited interoperability between implementations.

In 2023, the GraphQL Foundation’s Composite Schemas Working Group began developing a common, vendor-neutral specification.

Engineers from across the GraphQL ecosystem, including Apollo GraphQL, ChilliCream, Graphile, Hasura, Netflix, The Guild, and WunderGraph, contribute to this work, which has evolved into the GraphQL Federation specification.

The specification builds on the foundations established by Apollo Federation and GraphQL Fusion while moving toward a common standard for schema composition and distributed query execution.

An important design goal is to make ordinary GraphQL servers first-class participants in federation. Rather than requiring specialized reference resolvers or hidden entity fields, the specification describes federation semantics through the GraphQL schema and uses standard GraphQL operations for communication between services.

The standardization effort also includes work on GraphQL-over-HTTP capabilities, such as variable batching, that can support efficient distributed execution while remaining useful to GraphQL applications beyond federation.

GraphQL Federation is being developed as an open specification, allowing different servers, gateways, and tooling providers to implement a common federation model.

The specification is currently under active development and may evolve as it progresses through the standardization process.

Backward compatibility has been a key consideration in the design of the GraphQL Federation specification. The goal is to preserve existing investments in Apollo Federation while allowing organizations to adopt the new protocol incrementally, one subgraph at a time. This enables existing Apollo Federation subgraphs and new GraphQL Federation subgraphs to coexist within the same graph, provided the gateway supports both protocols, without requiring a coordinated migration.

For more information, see the GraphQL Federation specification.

Is GraphQL Federation right for your application?

GraphQL Federation is particularly useful when multiple teams need to develop and deploy different parts of a shared GraphQL API independently.

However, federation introduces additional complexity.

A federated architecture requires a gateway, schema composition, and coordination of distributed requests. Queries spanning multiple subgraphs may introduce network latency and additional failure scenarios that do not exist within a single process.

A single GraphQL server, including a well-structured modular monolith, can remain an excellent choice when independent service deployments are unnecessary.

Federation becomes valuable when the benefits of independent ownership, deployment, and scaling outweigh the operational complexity of a distributed system.

Importantly, adopting federation does not require moving every service into a separate deployment at once. Applications can begin with a single subgraph and evolve incrementally as their requirements change.

Getting started with GraphQL Federation

If you’re considering GraphQL Federation, start by identifying the domains and services that should contribute to your graph.

  1. Identify domain boundaries. Determine which teams or services own different parts of your API.
  2. Define source schemas. Describe the types and fields contributed by each subgraph, including entity keys and lookups where appropriate.
  3. Compose your graph. Use a federation composition tool to validate the source schemas and produce a composite schema.
  4. Configure a gateway. Expose the composite schema through a GraphQL gateway that can execute queries across the participating services.
  5. Evolve incrementally. Introduce new subgraphs as independent ownership and deployment become valuable.

If you already have a GraphQL server, you can often begin by using that server as your first subgraph and adding other services over time.

GraphQL Federation enables organizations to grow from a single GraphQL API into a distributed architecture without requiring clients to understand the services behind it.

For implementation details and the current status of the specification, visit the GraphQL Federation specification repository.

next lesson

Robust Applications

Build applications that gracefully handle schema evolution — unknown enum values, new union members, and nullable fields.

Go to next lesson