Schema merging
Schema merging (@graphql-tools/merge
) consolidates the type definitions and resolvers from many local schema instances into a single executable schema. This is useful for building a local service schema from many individually-managed parts. This should not be confused with schema stitching, which builds a combined proxy schema atop numerous service APIs.
#
Getting startedYou can use mergeSchemas
to merge GraphQLSchema
objects together with extra typeDefs
and resolvers
.
There is also mergeSchemasAsync
as a faster asynchronous alternative.
#
Merging type definitionsOriginally implemented in graphql-modules. This tool merges GraphQL type definitions and schema. It aims to merge all possible types, interfaces, enums and unions, without conflicts.
Let's say this is your current schema:
Knowing that your app will grow, you want to move your definitions to separate files that should look like the following.
There are two ways you can use this package:
- manually import each type
- import everything from a specified folder
#
Manual importsIf you decide to have manual control of each file that gets merged, all you need is the mergeTypeDefs(types)
function from @graphql-tools/merge
package:
See mergeTypeDefs
for more details.
#
File loadingIn this way we use the loadFilesSync
function from @graphql-tools/load-files
to import all files from the specified folder.
When using the loadFilesSync
function you can also implement your type definitions using .graphql
or .gql
or .graphqls
files.
You can also load files with specified extensions by setting the extensions option.
Only these values are supported now. 'ts', 'js', 'gql', 'graphql', 'graphqls'
By default, the
loadFilesSync
function will not ignore files namedindex.js
orindex.ts
, but you can set theignoreIndex
option totrue
to enable this behavior. This allows you to create your index file inside the actual types folder if desired.
You can also load files in nested folders by setting the recursive
option.
Given the file structure below:
Here's how your index
file could look like:
You can also load files in different folders by passing a glob pattern in loadFilesSync
.
Given the file structure below:
Here's how your index
file could look like:
#
Print merged typeDefsSince the output of mergeTypeDefs
is a GraphQL DocumentNode
, you may print the merged result as a string to be passed around to other systems. For example:
#
Nested TypesThe mergeTypeDefs
function also allows merging multiple schemas. In situations where you would like to have nested subfolders, you can merge your types by subfolder, and then everything into one single schema. For example:
#
DirectivesMerged directives will be stacked on top of each other, in the order of declaration. For example:
Becomes:
#
Merging resolversResolvers are implemented as simple JS objects and then merged using deep-merge. Resolver implementations can be separated across multiple objects and then merged into a single resolvers
object. Following the previous examples, for the types we implemented our resolvers should look like the following:
Just like your type definitions, you can choose to import files manually:
Or automatically:
Beware that
mergeResolvers
is simply merging plain Javascript objects together. This means that you should be careful with Queries, Mutations or Subscriptions with naming conflicts.
You can also load files with specified extensions by setting the extensions option.
Only these values are supported now: ts, js, gql, graphql, graphqls
.
Optional: Automatic with Resolver Naming Convention
If you would like to use the automated fileLoader
approach but would like complete
freedom over the structure of your resolver files, then simply use a resolver file naming
convention like, [file].resolvers.js/ts
.
Then setup your fileLoader
like so, and you're in business:
With this approach, you're free to structure resolver files as you see fit. Of course, unique naming of Queries, Mutations and Subscriptions still applies!
Now you can structure by function...
Or by type...