Schema stitching (
@graphql-tools/stitch) creates a single GraphQL gateway schema from multiple underlying GraphQL services. Unlike schema merging, which simply combines local schema instances, stitching builds a combined proxy layer that delegates requests through to underlying service APIs. As of GraphQL Tools v7, stitching is fairly comparable to Apollo Federation with automated query planning, merged types, and declarative schema directives.
Note that schema stitching is a superset of the schema wrapping API. Rather than wrapping schemas individually and then combining them,
stitchSchemas may be used directly to handle all wrapping concerns.
One of the main benefits of GraphQL is that we can query for all data in a single request to one schema. As that schema grows though, it may become preferable to break it up into separate modules or microservices that can be developed independently. We may also want to integrate the schemas we own with third-party schemas, allowing mashups with external data.
In these cases,
stitchSchemas is used to combine multiple GraphQL APIs into one unified gateway proxy schema that knows how to delegate parts of a request to the relevant underlying subschemas. These subschemas may be local GraphQL instances or APIs running on remote servers.
In this example we'll stitch together two very simple schemas representing a system of users and posts. You can find many supporting examples of stitching concepts in the Schema Stitching Handbook.
This process builds two GraphQL schemas, places them each into subschema configuration wrappers (discussed below), and then passes the subschemas to
stitchSchemas to produce one combined schema with the following root fields:
We now have a single gateway schema that allows data from either subschema to be requested in the same query.
In the example above, the extra "subschema" wrapper objects may look verbose at first glance, but they are actually basic implementations of the
SubschemaConfig interface that accepts several additional settings (discussed throughout this guide):
Subschema config should directly provide as many settings as possible to avoid unnecessary layers of delegation. For example, while we could pre-wrap a subschema with transforms and a remote executor, that would be far less efficient than providing the
executor options directly to subschema config.
Also note that these subschema config objects may need to be referenced again in other stitching contexts, such as schema extensions. With that in mind, you may want to export your subschema configs from their module(s).
To include a remote schema in the combined gateway, you must provide at least the
executor subschema config options, and an optional
subscriber for subscriptions:
schema: this is a non-executable schema representing the remote API. The remote schema may be obtained using introspection, or fetched as a flat SDL string (from a server or repo) and built into a schema using
buildSchema. Note that not all GraphQL servers enable introspection, and those that do will not include custom directives.
executor: is a generic method that performs requests to a remote schema. It's quite simple to write your own. Subschema config uses the executor for query and mutation operations. See handbook example.
subscriber: to enable subscription operations, include a subscriber function that returns an AsyncIterator. See handbook example.
Stitching has two strategies for handling types duplicated across subschemas: an automatic merge strategy (default), and an older manual resolution strategy. You may select between these strategies using the
Types with the same name are automatically merged by default in GraphQL Tools v7. That means objects, interfaces, and input objects with the same name will consolidate their fields across subschemas, and unions/enums will consolidate all of their members. The combined gateway schema will then smartly delegate portions of a request to the proper origin subschema(s). See type merging guide for a comprehensive overview.
Automatic merging will only encounter conflicts on type descriptions and fields. By default, the final definition of a type or field found in the subschemas array is used, or a specific definition may be marked as canonical to prioritize it. You may customize all selection logic using
typeMergingOptions; the following example prefers the first definition of each conflicting element found in the subschemas array:
The automatic merge strategy also validates the integrity of merged schemas. Validations may be set to
off for the entire schema or scoped for specific types and fields:
mergeTypes: false, only the final description and fields for a type found in the subschemas array will be used, and automated query planning will be disabled. You may manually resolve differences between conflicting types with an
Another strategy to avoid conflicts while combining schemas is to modify one or more of the subschemas using transforms. Transforming allows a schema to be groomed in such ways as adding namespaces, renaming types, or removing fields (to name a few) prior to stitching it into the combined gateway schema. These transforms should be added directly to subschema config:
In the example above, we transform the
postsSchema by removing the
postsByUserId root field and adding a
Post_ prefix to all types in the schema. These modifications will only be present in the combined gateway schema.
Note that when automatically merging types, all transforms are applied prior to merging. That means transformed types will merge based on their transformed names within the combined gateway schema.
Whether you're merging types, using schema extensions, or simply combining schemas, any errors returned by a subschema will flow through the stitching process and report at their mapped output positions. It's fairly seamless to provide quality errors from a stitched schema by following some basic guidelines:
Report errors! Having a subschema return
nullwithout an error for missing or failed records is a poor development experience to begin with. This omission will compound should an unexpected value produce a misleading failure in gateway stitching. Reporting proper GraphQL errors will contextualize failures in subschemas, and by extension, within the stitched schema.
Map errors to array positions. When returning arrays of records (a common pattern while batch loading), make sure to return errors for specific array positions rather than erroring out the entire array. For example, an array should be resolved as:
- Assure valid error paths. The GraphQL errors spec prescribes a
pathattribute mapping an error to its corresponding document position. Stitching uses these paths to remap subschema errors into the combined result. While GraphQL libraries should automatically configure this
pathfor you, the accuracy may vary by programming language.