Introduction to Apollo Federation
Implement a single graph across multiple services
đŸ“£ Apollo Federation 2 is generally available! View the Federation 2 docs.
To get the most out of GraphQL, your organization should expose a single graph that provides a unified interface for querying any combination of your backing data sources. However, it can be challenging to represent an enterprise-scale graph with a single, monolithic GraphQL server.
To remedy this, you can use Apollo Federation to divide your graph's implementation across multiple back-end services (called subgraphs):
Unlike other distributed GraphQL architectures (such as schema stitching), Apollo Federation uses a declarative programming model that enables each subgraph to implement only the part of your composed supergraph that it's responsible for.
Apollo Federation also supports a free managed mode with Apollo Studio, which enables you to add, remove, and refactor your subgraphs without requiring any downtime for your production graph.
Architecture
An Apollo Federation architecture consists of:
A collection of subgraphs (usually represented by different back-end services) that each define a distinct GraphQL schema
A gateway that uses a supergraph schema (composed from all subgraph schemas) to execute queries across multiple subgraphs
Apollo Server provides libraries for acting both as a subgraph and as a gateway, but these components can be implemented in any language and framework.
Apollo Federation does not currently support GraphQL subscription operations.
The following presentation by Mandi Wise further describes the architecture of Apollo Federation and walks through implementing a federated graph:
Design principles
Incremental adoption
Like the rest of the Apollo platform, Apollo Federation can (and should) be adopted incrementally:
If you currently use a monolithic GraphQL server, you can break its functionality out one service at a time.
If you currently use a different federated architecture (such as schema stitching), you can add federation support to your existing services one at a time.
In both of these cases, all of your clients will continue to work throughout your incremental migration. In fact, clients have no way to distinguish between different graph implementations.
Separation of concerns
Apollo Federation encourages a design principle called separation of concerns. This enables different teams to work on different products and features within a single graph, without interfering with each other.
Limitations of type-based separation
When considering how to split a single GraphQL schema across multiple subgraphs, it seems straightforward to divide schemas up by type. For example, a users
subgraph would define the entirety of a User
type, the products
subgraph would define a Product
type, and so on:
Although this separation looks clean, it quickly causes issues. Specifically, a particular feature (or concern) usually spans multiple types.
Consider the recentPurchases
field of the User
type in the above schema. Even though this field is a member of the User
type, a list of Product
s should probably be populated by the products
subgraph, not the users
subgraph.
By defining the recentPurchases
field in the products
subgraph instead:
The subgraph that defines the field is also the subgraph that knows how to populate the field. The
users
subgraph might not even have access to the back-end data store that contains product data.The team that manages product data can contain all product-related logic in a single subgraph that they own unilaterally.
Concern-based separation
The following schema uses Apollo Federation to divide the same set of types and fields across the same three subgraphs:
The difference is that now, each subgraph defines the types and fields that it is capable of (and should be responsible for) populating from its back-end data store.
The result is the best of both worlds: an implementation that keeps all the code for a given feature in a single subgraph and separated from unrelated concerns, and a product-centric schema with rich types that reflects the natural way an application developer would want to consume the graph.
Federated schemas
A federated graph uses multiple "types" of GraphQL schemas:
Subgraph schemas. Each of your subgraphs has a distinct schema that indicates which types and fields of your composed supergraph it's responsible for resolving.
Supergraph schema. This schema is the result of performing composition on your collection of subgraph schemas. It combines all of the types and fields from your subgraph schemas, plus some federation-specific directives that tell your gateway which subgraphs are responsible for resolving which fields.
API schema. This schema is like the supergraph schema, but it omits types, fields, and directives that are considered "machinery" and are not part of your public API (this includes federation-specific directives).
This is the schema that your gateway exposes to your GraphQL API's consumers, who don't need to know any internal implementation details about your graph.
Let's look at an example!
Subgraph schemas
Below, we define the schemas for three subgraphs in a basic e-commerce application. Each subgraph is implemented as a standalone back-end service:
1extend type Query {
2 me: User
3}
4
5type User @key(fields: "id") {
6 id: ID!
7 username: String!
8}
1extend type Query {
2 topProducts(first: Int = 5): [Product]
3}
4
5type Product @key(fields: "upc") {
6 upc: String!
7 name: String!
8 price: Int
9}
1type Review {
2 body: String
3 author: User @provides(fields: "username")
4 product: Product
5}
6
7extend type User @key(fields: "id") {
8 id: ID! @external
9 username: String! @external
10 reviews: [Review]
11}
12
13extend type Product @key(fields: "upc") {
14 upc: String! @external
15 reviews: [Review]
16}
These subgraph schemas illustrate several important conventions of Apollo Federation:
A subgraph can reference a type that's defined by another subgraph. For example, the
Review
type includes aproduct
field of typeProduct
, even though theProduct
type is defined in a different subgraph.A subgraph can also extend a type that's defined by another subgraph. For example, the
reviews
subgraph extends theUser
type by adding areviews
field to it.A subgraph must add the
@key
directive to an object type's definition in order for other subgraphs to be able to reference or extend that type. This directive makes an object type an entity.
Supergraph schema
To create our supergraph schema, we perform composition on our collection of subgraph schemas. With managed federation, Apollo performs composition automatically whenever one of your subgraphs registers an updated schema.
We can also perform composition manually with the Rover CLI:
1rover supergraph compose --config ./supergraph.yaml
For an example configuration file, see the Rover docs.
This outputs the following supergraph schema that our gateway can use to route queries to the correct subgraphs:
Click to expand
1schema
2 @core(feature: "https://specs.apollo.dev/core/v0.2"),
3 @core(feature: "https://specs.apollo.dev/join/v0.1", for: EXECUTION)
4{
5 query: Query
6}
7
8directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
9
10directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet) on FIELD_DEFINITION
11
12directive @join__type(graph: join__Graph!, key: join__FieldSet) repeatable on OBJECT | INTERFACE
13
14directive @join__owner(graph: join__Graph!) on OBJECT | INTERFACE
15
16directive @join__graph(name: String!, url: String!) on ENUM_VALUE
17
18enum core__Purpose {
19 EXECUTION
20 SECURITY
21}
22
23scalar join__FieldSet
24
25enum join__Graph {
26 ACCOUNTS @join__graph(name: "accounts" url: "http://localhost:4001")
27 PRODUCTS @join__graph(name: "products" url: "http://localhost:4002")
28 REVIEWS @join__graph(name: "reviews" url: "http://localhost:4003")
29}
30
31type Product
32 @join__owner(graph: PRODUCTS)
33 @join__type(graph: PRODUCTS, key: "upc")
34 @join__type(graph: REVIEWS, key: "upc")
35{
36 name: String! @join__field(graph: PRODUCTS)
37 price: Int @join__field(graph: PRODUCTS)
38 reviews: [Review] @join__field(graph: REVIEWS)
39 upc: String! @join__field(graph: PRODUCTS)
40}
41
42type Query {
43 me: User @join__field(graph: ACCOUNTS)
44 topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCTS)
45}
46
47type Review {
48 author: User @join__field(graph: REVIEWS, provides: "username")
49 body: String
50 product: Product
51}
52
53type User
54 @join__owner(graph: ACCOUNTS)
55 @join__type(graph: ACCOUNTS, key: "id")
56 @join__type(graph: REVIEWS, key: "id")
57{
58 id: ID! @join__field(graph: ACCOUNTS)
59 reviews: [Review] @join__field(graph: REVIEWS)
60 username: String! @join__field(graph: ACCOUNTS)
61}
As you can see, the supergraph schema includes a lot of federation-specific additions! These additions are used only by the gateway, and you'll never need to add them manually.
API schema
The gateway uses its supergraph schema to produce an API schema, which is what's exposed to clients as your actual GraphQL API. This schema cleanly and logically represents the combination of your subgraph schemas:
Click to expand
1type Product {
2 name: String!
3 price: Int
4 reviews: [Review]
5 upc: String!
6}
7
8type Query {
9 me: User
10 topProducts(first: Int = 5): [Product]
11}
12
13type Review {
14 author: User
15 body: String
16 product: Product
17}
18
19type User {
20 id: ID!
21 reviews: [Review]
22 username: String!
23}
Gateway example
You provide a composed supergraph schema to the ApolloGateway
constructor, like so:
1const supergraphSdl = readFileSync('./supergraph.graphql').toString();
2
3const gateway = new ApolloGateway({
4 supergraphSdl
5});
6
7const server = new ApolloServer({ gateway });
8server.listen();
That’s it! With Apollo Federation, resolvers live in your subgraphs. The gateway serves only to plan and execute GraphQL operations across those subgraphs.
Now we can execute GraphQL operations against our gateway just as if it were implemented as a single, monolithic GraphQL server:
1# A query that the gateway resolves by calling all three services
2query GetCurrentUserReviews {
3 me {
4 username
5 reviews {
6 body
7 product {
8 name
9 upc
10 }
11 }
12 }
13}
Managed federation
In addition to providing its supergraph schema on startup, Apollo Gateway can operate in managed federation mode, where Apollo Studio acts as the source of truth for each subgraph's schema.
This mode enables multiple teams working on a graph to coordinate when and how underlying subgraphs change. It's recommended for all federated graphs. For more information, read Managed federation overview.
Apollo Server libraries
Apollo Server supports Apollo Federation via two open-source extension libraries:
@apollo/subgraph
provides primitives that subgraphs use to make their individual GraphQL schemas composable.@apollo/gateway
enables you to set up an instance of Apollo Server as a gateway that distributes incoming GraphQL operations across one or more subgraphs.
Ready to try out Apollo Federation? Jump into the Quickstart!