Changes from Apollo Federation 1
Learn what's new in Apollo Federation 2
Apollo Federation 2 provides developer experience improvements to the original specification for Apollo Federation (called Federation 1 in these docs). If your organization has an existing Federation 1 graph, this article summarizes the benefits of moving to Federation 2.
💡 TIP
If you're just getting started with Federation, check out the Quickstart.
What isn't changing?
Before covering what's new, here's what isn't changing in Federation 2:
Most importantly, Federation 2 is backward compatible with most Federation 1 supergraphs. You can probably move your existing supergraph to use Federation 2 composition without making any changes.
- Graphs that do require changes are graphs that should cause composition errors, but Federation 1 fails to detect them. Learn more.
- To take full advantage of Federation 2 features, you do need to make some changes to your subgraph schemas, but you can make these changes incrementally at your convenience. See Moving to Apollo Federation 2.
Subgraph servers have no additional requirements. Any subgraph-compatible library is automatically compatible with Federation 2.
- Many subgraph-compatible libraries do not yet automatically define certain directives that are new in Federation 2, such as
@shareable
. You can define these directives manually to use them in your subgraph schemas.
- Many subgraph-compatible libraries do not yet automatically define certain directives that are new in Federation 2, such as
More flexible composition
Federation 2 improves the flexibility and independence of your subgraph schemas. New composition logic is more flexible compared to Federation 1, meaning teams can more confidently build out their subgraphs or migrate functionality between subgraphs.
Let's look at some examples!
Value types
In Federation 1, multiple subgraphs can define the same type, but those definitions must all be identical. These identical shared types are called value types:
Fed. 1
type Book {title: String!author: String!}
type Book {title: String!author: String!}
In Federation 2, this "identical definition" constraint is removed. Value types and their fields can be shared across subgraphs, even if certain details differ between definitions.
For details, see the sections below, along with Value types.
Objects
In Federation 2, an object type can be shared between subgraphs like so:
Fed. 2
type Book @shareable {title: String!author: String!}
type Book @shareable {title: String!author: String # Nullableisbn: String! # Not in A}
The two Book
type definitions above differ in terms of the fields they include and the nullability of those fields. Notice also the new @shareable
directive, which is required to indicate that a field can be resolved by multiple subgraphs.
ⓘ NOTE
Marking a type as @shareable
(as with Book
above) is equivalent to marking all of its fields as @shareable
.
This flexibility is especially helpful when an organization has multiple standalone GraphQL APIs that they want to unify with federation. Such APIs often share some types, and this added flexibility reduces the work required to compose their schemas successfully.
Valid shared field differences between subgraphs
- The return type of a shared field can vary in nullability (
String
/String!
). - Types can omit fields that are included in other subgraphs, as long as every field in your supergraph is always resolvable. (For details, see Rules of composition.)
For details on how these field differences are handled, see Differing shared fields.
Enums, unions, and interfaces
In Federation 2, enum
, union
, and interface
definitions can differ between subgraphs. For details, see Merging types from multiple subgraphs.
Entities
Federation 2 introduces subtle but powerful changes to entities.
Originating subgraphs
In Federation 1, an entity originates in one subgraph and is then extended in other subgraphs:
Fed. 1
type Product @key(fields: "id") {id: ID!name: String!price: Int}
extend type Product @key(fields: "id") {id: ID! @externalinStock: Boolean!}
In Federation 2, entities don't have an "originating subgraph." Instead, each subgraph can define an entity and contribute fields to it:
Fed. 2
type Product @key(fields: "id") {id: ID!name: String!price: Int}
type Product @key(fields: "id") {id: ID!inStock: Boolean!}
For details, see Contributing entity fields.
Shared entity fields
In Federation 1, each entity field is resolved by exactly one subgraph (unless it's a @key
field or part of a @provides
directive). In Federation 2, multiple subgraphs can resolve the same entity field if they all mark it with the @shareable
directive:
Fed. 2
type Product @key(fields: "id") {id: ID!name: String! @shareableprice: Int}
type Product @key(fields: "id") {id: ID!name: String! @shareableinStock: Boolean!}
For more information, see Resolving another subgraph's field.
Changes to @key
Federation 2 adds a new optional argument to the @key
directive: resolvable
. You can set this argument to false
to indicate that a particular subgraph doesn't define a reference resolver for that entity.
For example:
Fed. 2
type Product @key(fields: "id") {id: ID!name: String!price: Int}
type Product @key(fields: "id", resolvable: false) {id: ID!}type Review {product: Product!score: Int!}
In this case, the Reviews subgraph references the Product
entity by using it as the return type for Review.product
. However, the subgraph doesn't contribute any unique fields to Product
, so it doesn't need to define a reference resolver for it. Therefore, the "stub" definition of Product
in the Reviews subgraph can include resolvable: false
.
For more information, see Referencing an entity without contributing fields.
Migrating fields
Federation 2 introduces the @override
directive, which helps you safely migrate entity and root-level fields between subgraphs with managed federation:
type Bill @key(fields: "id") {id: ID!amount: Int!}type Payment {# ...}
type Bill @key(fields: "id") {id: ID!amount: Int! @override(from: "Payments")}
For details, see Entity migration.
Interfaces implementing interfaces
Federation 1 composition incorrectly fails if an interface type implements another interface. This issue is resolved in Federation 2:
Fed. 2
interface Media {title: String!}interface Book implements Media {title: String!author: String!}