Migrating from schema stitching
How to move your services to Apollo Federation
If you have a distributed data graph that uses schema stitching, follow the steps in this guide to migrate it to use Apollo Federation.
For details on the advantages of using a federated data graph instead of schema stitching, see this blog post.
Summary of steps
You can (and should) migrate incrementally from schema stitching to Apollo Federation. To do so, you run an Apollo Server gateway alongside your existing schema-stitching gateway and migrate the services that implement your data graph (implementing services) one at a time.
Here are the high-level steps for migrating to Apollo Federation:
- Add federation support to your implementing services
- Register your GraphQL schemas with a registry
- Start up an instance of Apollo Server as a gateway
- Migrate stitching logic from your schema-stitching gateway to your implementing services
- Move traffic from the schema-stitching gateway to the Apollo Server gateway
- Remove schema-stitching fields from your federated schema and complete your migration
Each step is described in detail below.
This GitHub repository shows the same project before and after migrating to Apollo Federation from schema stitching.
Step 1: Add federation support to your implementing services
You can add federation support to your implementing services without impacting your existing schema-stitching architecture. Support for federation is fully compatible with schema stitching.
Because of this, we recommend that you migrate your implementing services in place instead of creating replacement services. Doing so helps you identify any type conflicts that exist across your data graph.
Using Apollo Server
If your implementing services use Apollo Server, add federation
support to them by installing the @apollo/federation
package:
npm install @apollo/federation
Then use the buildFederatedSchema
function to augment your schema with
fields that are necessary for federation support:
const { ApolloServer } = require('apollo-server');
const { buildFederatedSchema } = require('@apollo/federation');
const server = new ApolloServer({
schema: buildFederatedSchema([
{
typeDefs,
resolvers,
},
]),
});
Using a GraphQL server besides Apollo Server
There are several community-contributed packages that add federation support to other GraphQL runtimes. These include:
If you're using one of these packages, ensure that after configuring it, your existing schema-stitching gateway continues to work correctly.
Step 2: Register your schemas with a GraphQL registry
We strongly recommend that you register all of your GraphQL schemas with an external registry. Doing so improves the reliability of your data graph and maintains a single source of truth to simplify collaboration.
Apollo Graph Manager provides a free schema registry that helps you manage your federated gateway's configuration. You provide your gateway a Graph Manager API key on startup, which directs the gateway to download your schemas automatically in a fault-tolerant way.
Graph Manager can also provide schema validation to ensure that all changes you make to your implementing services are compatible with your complete data graph.
Step 3: Start up an Apollo Server gateway
After you've registered your schemas, you can start exposing your implementing services from a federation-compatible gateway. Apollo Server's gateway is a query planner and executor that handles incoming GraphQL requests and breaks them down into a collection of operations to perform on your implementing services.
We recommend setting up the Apollo Server gateway alongside your existing schema-stitching gateway. Depending on your infrastructure, you might even want to run both in the same process to support dynamically routing traffic through one gateway or the other.
To enable managed configuration through Apollo Graph Manager, set the ENGINE_API_KEY
and ENGINE_SCHEMA_TAG
environment variables when you start up your Apollo Server gateway, and do not provide the serviceList
constructor option to ApolloGateway
. For details, see the Graph Manager documentation.
After your gateway is set up, you can make direct queries to it that are routed to the correct implementing services.
Step 4: Move linking logic to your implementing services
When using a schema-stitching gateway, your linking logic typically resides in the gateway itself. In the federation model, however, linking logic resides in each implementing service. Therefore, you need to migrate linking logic from your schema-stitching gateway into each of your implementing services.
Here are recommendations for common cases when migrating your logic:
- Fragments: Fragments in a schema-stitching resolver, usually translate to a combination of
@key
and@requires
directives in a federated model. In general, think of@key
as the field(s) that completely identify an entity, and only use@requires
for additional, non-identifying information. - Filtering types: We do not recommend filtering types out of your exposed schema when using a gateway. If you want to hide types, do not include them in your service's registered schema.
- Renaming types: If you are currently renaming types at the gateway level, rename these types at the service level instead.
- Transforming fields: If you are currently transforming fields at the gateway level, transform these fields at the service level instead.
Adding resolvers to your federated services
At this point your implementing services support federation, but they still need to be able to resolve extensions to types that are defined in other services.
A schema-stitching architecture declares this logic at the gateway level using the
delegateToSchema
function, like so:
resolvers: {
Reservation: {
user: {
fragment: `... on Reservation { userId }`,
resolve: (parent, args, context, info) => {
return info.mergeInfo.delegateToSchema({
schema: userSchema,
operation: 'query',
fieldName: 'user',
args: {
id: parent.userId,
},
context,
info,
});
},
},
},
}
This resolver calls Query.user
on the userSchema
to look up a User
. It adds that user to the Reservation.user
field that was previously defined at the gateway. This code can all remain. You don't need to remove it from the stitched gateway. In fact, if you did that, the stitched gateway would break.
On the other hand, a federated architecture defines its resolvers at the service level. These
resolvers rely on entities, which are identified by a primary key. For example, the Reservation
service must define the Reservation
type as an entity to allow other services
to extend it. These other services use the Reservation
's @key
fields to uniquely
identify a given instance:
type Reservation @key(fields: "id") {
id: ID!
...
}
In the Users service, you can then extend the Reservation
type with a user
field like so:
extend type Reservation @key(fields: "id") {
id: ID! @external
userId: ID! @external
user: User @requires(fields: "userId")
}
The user
field indicates that it @requires
a Reservation
's userId
field in order to identify the user that made the reservation.
Then in the Users service, you can add a resolver for Reservation.user
like so:
{
Reservation: {
user: ({ userId }) => {
return lookupUser(userId);
},
}
}
Federated resolvers like this one always receive an object that represents an instance of the extended entity. This object includes the fields that are part of
the entity's @key
, along with any other fields that the resolver @requires
.
For example, this Reservation.user
resolver receives the id
of the reservation and a userId
. You can use the userId
to look up the corresponding user.
Step 5: Move traffic from the schema-stitching gateway to the Apollo Server gateway
At this point, both your schema-stitching gateway and your federated gateway are able to resolve GraphQL operations. You can now begin moving traffic from the schema-stitching gateway to the federated gateway.
Perform this migration in the manner that best suits your infrastructure and applications.
Some options include:
- Testing a complete migration in your staging environment to verify that both gateways behave identically
- Use HTTP headers or feature flags to migrate your internal clients without affecting your user-facing clients
Step 6: Remove schema-stitching fields from your federated schema
After you've fully migrated your graph and incoming traffic to use your federated gateway, you can remove all stitching-specific logic from your architecture.
You can now begin to modify your existing schema to take full advantage of the features that federation provides. These features include:
- Greater flexibility with federation core concepts
- Metrics and analysis of query plans
- Gateway support for live schema updates from implementing services
- Validation of composition logic and usage traffic (with paid subscription)