Resolvers are functions that make up the basis for how data is determined in resolving queries in GraphQL APIs. There
are two types of resolvers, Field Resolvers and Type Resolvers. Field Resolvers represent the specific data
returned within the
data key a result payload. Type Resolvers are responsible for taking an Abstract Type (GraphQL
Union or Interface types) and returning a string representing the concrete type.
Field resolvers are represented by the following function:
The majority of the time when Resolvers are mentioned in a GraphQL context it is referring to Field Resolvers. Field
Resolvers are responsible for returning the
data specified by the GraphQL Schema (or a
Promise of the data). Check
out the apollo documentation on resolvers, too, it
applies here and to GraphQL in general.
With the given schema:
This schema would have two resolvers:
- One "root field resolver" on the
Querytype for the
- One "non-root field resolver" on the
Actortype for the
Root (Query and Mutation) Field Resolvers
All GraphQL queries start with a "root" field. Root Field Resolvers are either on the root
Query type or the root
Mutation type depending on whether it's a query or mutation operation, respectively. These root resolvers "kick off"
the query and being at the "root" typically have a
null parent argument (unless one has been explicitly declared).
A root resolver function for the
actor field could be:
Non-Root Field Resolvers
Any type is that not the root Query type or the root Mutation type will be a type that contains non-root field
resolvers. In this case they are connected to the graph through some reference to the root Query and Mutation types.
Due to the graph-like nature of resolving queries these non-root fields and their resolvers can be referenced multiple
times and as such often rely on the
parent parameters to understand their connection in the graph.
The non-root field on
name. When any type resolves an
Actor type each of the field resolvers receives
the parent and is responsible for returning a result for their field.
name field resolver on the
Actor type could look like:
This example looks for the
name property on the parent. In this case the
The returned value is
"Meryl Streep" which matches the return value of
String! set by the
name field on the
Now, to show the flexible nature of resolvers lets expand the the
Here we have the
favoriteActor of an
Actor being another
Actor. This demonstrates the how types can reference
each other and resolver functions have to be able to return the relevant data whether called from a Root Query or Root
Mutation type, or from another type, or the same type itself.
If Tom Hanks was Meryl Streep's favorite actor the
favoriteActor resolver could look like:
Only when the parent
Actor has a name of
"Meryl Streep" do we return
"Tom Hanks" in other cases we will return
null for all other
Actors. This could be quite tedious to manage all the favorite actors from a single resolver so
it is usually best practice to have a data source or a well-referenced look-up that can be used. Typically, the parent
also has an
ID field that can be referenced for lookup also.
Default Field Resolver
It should be noted that GraphQL sets up every field with a default Field Resolver function. In fact it can be imported
graphql package, too.
It will attempt to resolve a field based on the a look up of the field's name on the parent. This typically a desired behavior so often you don't have to write a resolver at all, graphql will do this "out of the box".
There are a couple of other things the default Field Resolver does, check out the source code, it's under 10 lines!
parent parameter (first)
obj, and be named whatever you call the variable, represents the previous returned value in the
"graph" of results. As mentioned, in root field resolvers this will typically be null since the query has just begun and
has no parent.
arg parameter (second)
When a schema specifies arguments and those values are provided they are available on the field by its
Instead of defaulting to "Meryl Streep" what if we had a list of actors required the name to be specified and returned
the actor that matched.
And the value for name can be referenced as an argument:
The source of this
actors data is a bit hand-wavy, there are ways of accessing these data sources (typically from the
context parameter), but what is important is how arguments are passed to the field and its resolver via the
context parameter (third)
This is the "global" bucket that is accessible in every resolver. It's usually an object. It's typically a good place to
put data sources that can combined with the
args can be used to lookup.
graphql-mocks has an opinion
approach with a managed context to provide conventions and assist in threading the common use cases.
dependencies that are passed to the graphql-mocks GraphQL Handler
can reliably be pulled from context using the
There are other helpful ways that context can be used with
info parameter (fourth)
This is mostly a holder of meta and GraphQL information. It's not usually needed but does have some handy insights into the query, AST, and access to the GraphQL schema.
While covered less the
resolveType Type Resolver function on the
GraphQLUnionType play an important role in resolving GraphQL
queries. See the Apollo documentation for
Type Resolver functions have the following signature:
The objective for a Type Resolver is given a "value", the object in question, to determine which discrete type is
represented. The return value of a Type Resolver function is a string representing the concrete type name, ie:
Cat in the following example for the
Pet abstract type.
Here's an example:
And our Type Resolver:
We are given the concrete object in question and are responsible for determining which type of
Pet it is: either a Pet
Dog or a
Cat. In this contrived example the
likesBones property is checked to exist and if it does we assume
Dog. This is something to consider when working with Abstract Types (Unions and Interfaces) is how the
underlying concrete types will be identified. It is usually to leave a concrete type identifier
__typename on the
concrete type which would allow the default type resolver to automatically use the value.
value parameter (first)
The ambiguous object in question that needs to be identified to a concrete type
context parameter (second)
context global object as represented in Field Resolvers.
info parameter (third)
Represents the meta details and GraphQL information, is usually only needed in exceptional cases.
abstractType parameter (fourth)
abstractType is either a
GraphQLUnionType and the instance provides the details of the
abstract type that is being resolved down to a concrete type.
Default Type Resolver
Similar to Field Resolvers, Type Resolvers also have a default resolver that exists on the
resolveType of the
Interface and Union types.
The default behavior the
defaultTypeResolver is to check the object for the
__typename field and return the value if
it exists, or less commonly to check
isTypeOf on each on each possible type with the object being coerced. Check out
for more details.
Organizing Resolvers for Executing GraphQL queries
This page focuses on the Resolver functions themselves but hasn't shown how they are actually applied to the Fields and
Abstract Types that they are responsible for resolving. On their own they are "just functions" and need a way of being
assigned. This is the role of a Resolver Map which organized these Resolver Functions so that they can be applied to a
GraphQLSchema and executed against queries and mutations, check out the
Resolver Map to understand the next step in applying Resolver functions.
Extending Resolvers with Resolver Wrappers
The next section will introduce Resolver Wrappers from this library and how they can be used to allow Resolvers functions to be extended.