Schema merging (@graphql-tools/merge and @graphql-tools/schema) consolidates the type
definitions and resolvers from many local schema instances into a single executable schema. This is
useful for building a single local service schema from many individually-managed parts. This should
not be confused with schema stitching, which builds a
combined proxy schema atop numerous subservice APIs.
Getting Started
You can use mergeSchemas to merge GraphQLSchema objects together with extra typeDefs and
resolvers.
Originally 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.
Knowing that your app will grow, you want to move your definitions to separate files that should
look like the following.
graphql/types/clientType.js
module.exports = /* GraphQL */ ` type Client { id: ID! name: String age: Int products: [Product] } type Query { clients: [Client] client(id: ID!): Client } type Mutation { addClient(name: String!, age: Int!): Client }`
graphql/types/productType.js
module.exports = /* GraphQL */ ` type Product { id: ID! description: String price: Int client: Client } type Query { products: [Product] product(id: ID!): Product }`
There are two ways you can use this package:
manually import each type
import everything from a specified folder
Manual Imports
If 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:
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'
Since 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:
The 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:
+-- graphql| +-- types| | +-- subGroupA| | | +-- index.js <<< Merges all types in subGroupA| | | +-- typeA1.graphql| | | +-- typeA2.graphql| | +-- subGroupB| | | +-- index.js <<< Merges all types in subGroupB| | | +-- typeB1.graphql| | | +-- typeB2.graphql| | +-- index.js <<< Merges exports from subGroupA and subGroupB
Directives
Merged directives will be stacked on top of each other, in the order of declaration. For example:
Resolvers 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:
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, the unique
naming of Queries, Mutations and Subscriptions still applies!