Executable Schemas
The GraphQL-Tools package allows you to create a GraphQL.js GraphQLSchema instance from GraphQL
schema language using the function makeExecutableSchema
.
Example
When using graphql-tools
, you describe the schema as a GraphQL type language string:
const typeDefs = /* GraphQL */ `
type Author {
id: Int!
firstName: String
lastName: String
"""
the list of Posts by this author
"""
posts: [Post]
}
type Post {
id: Int!
title: String
author: Author
votes: Int
}
# the schema allows the following query:
type Query {
posts: [Post]
author(id: Int!): Author
}
# this schema allows the following mutation:
type Mutation {
upvotePost(postId: Int!): Post
}
`
Then you define resolvers as a nested object that maps type and field names to resolver functions:
// example data
const authors = [
{ id: 1, firstName: 'Tom', lastName: 'Coleman' },
{ id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
{ id: 3, firstName: 'Mikhail', lastName: 'Novikov' }
]
const posts = [
{ id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 },
{ id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3 },
{ id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 },
{ id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7 }
]
const resolvers = {
Query: {
posts: () => posts,
author: (_, { id }) => authors.find(author => author.id === id)
},
Mutation: {
upvotePost(_, { postId }) {
const post = posts.find(post => post.id === postId)
if (!post) {
throw new Error(`Couldn't find post with id ${postId}`)
}
post.votes += 1
return post
}
},
Author: {
posts: author => posts.filter(post => post.authorId === author.id)
},
Post: {
author: post => authors.find(author => author.id === post.authorId)
}
}
Install the package
npm i @graphql-tools/schema
In the end, the schema and resolvers are combined using makeExecutableSchema
:
import { makeExecutableSchema } from '@graphql-tools/schema'
export const schema = makeExecutableSchema({ typeDefs, resolvers })
This example has the entire type definition in one string and all resolvers in one object, but you can combine types and resolvers from multiple files, as documented in the extending types section below.
Extending Types
It’s easy to add additional fields to existing types using the extend
keyword. Using extend
is
particularly useful in avoiding a large list of fields on root Queries and Mutations. You can use it
like this:
const typeDefs = [
/* GraphQL */ `
schema {
query: Query
}
type Query {
bars: [Bar]!
}
type Bar {
id
}
`,
/* GraphQL */ `
type Foo {
id: String!
}
extend type Query {
foos: [Foo]!
}
`
]
If one of the types extended needs a resolver you can use makeExecutableSchema
like this:
const barsResolver = {
Query: {
bars(parent, args, context, info) {
// ...
}
}
}
const foosResolver = {
Query: {
foos(parent, args, context, info) {
// ...
}
}
}
const schema = makeExecutableSchema({
typeDefs,
resolvers: [barsResolver, foosResolver]
})
Learning the GraphQL Schema Language
The official documentation on graphql.org now has a section about GraphQL schemas which explains all of the different schema features and how to use them with the schema language.
The type definitions must define a query type, which means a minimal schema would look something like this:
const typeDefs = [
/* GraphQL */ `
schema {
query: RootQuery
}
type RootQuery {
aNumber: Int
}
`
]
Descriptions & Deprecations
GraphiQL has built-in support for displaying docstrings with markdown syntax. You can easily add docstrings to types, fields and arguments like below:
"""
Description for the type
"""
type MyObjectType {
"""
Description for field
Supports multi-line description
"""
myField: String!
otherField(
"""
Description for argument
"""
arg: Int
)
oldField(
"""
Description for argument
"""
arg: Int
) @deprecated(reason: "Use otherField instead.")
}
This GraphQL schema language cheat sheet by Hafiz Ismail is an excellent reference for all the features of the GraphQL schema language.
API
makeExecutableSchema
Takes a single argument: an object of options. Only the typeDefs
option is required. It returns a
new schema, modified as specified.
import { makeExecutableSchema } from '@graphql-tools/schema'
const jsSchema = makeExecutableSchema({
typeDefs,
resolvers, // optional
logger, // optional
resolverValidationOptions: {}, // optional
parseOptions: {}, // optional
inheritResolversFromInterfaces: false // optional
})
-
typeDefs
is a required argument and should be a GraphQL schema language string or an array of GraphQL schema language strings or a function that takes no arguments and returns an array of GraphQL schema language strings. The order of the strings in the array is not important, but it must include a schema definition. -
resolvers
is an optional argument (empty object by default) and should be an object or an array of objects that follow the pattern explained in article on resolvers -
parseOptions
is an optional argument that allows customization of parse when specifyingtypeDefs
as a string. -
resolverValidationOptions
is an optional argument with the following properties, each of which can be set toerror
,warn
, orignore
:-
requireResolversForArgs
will causemakeExecutableSchema
to throw an error (error
) or issue a warning (warn
)unless a resolver is defined for every field with arguments. The default isignore
, causing this validator to be skipped. -
requireResolversForNonScalar
require a resolver for every non-scalar field. Default isignore
. -
requireResolversForAllFields
asserts that all fields have valid resolvers. This option cannot be set in combination with the previous two validators. Default isignore
. -
requireResolversForResolveType
will require aresolveType()
method for Interface and Union types. This can be passed in with the field resolvers as__resolveType()
. Default isignore
. -
requireResolversToMatchSchema
requires every resolver within the resolver map to correspond to a GraphQL entity within the schema. Defaults toerror
, to help catch common errors.
-
-
inheritResolversFromInterfaces
GraphQL Objects that implement interfaces will inherit missing resolvers from their interface types defined in theresolvers
object.