On this page you will find information about what GraphQL resolvers are and how they work. This page will be most useful for developers that are already familiar with GraphQL.
Resolvers exist in any GraphQL implementation, not just WPGraphQL, but how they are implemented and the API for working with them varies from language to language, which is why you find this page under the WPGraphQL Concepts section.
A GraphQL Schema consists of Types and Fields, which declares what is possible to be asked for. Resolvers are the functions that execute when a field is asked for.
When registering a field to the WPGraphQL Schema defining a resolver is optional.
Registering a field without a resolver:
Below is an example of registering a field to the Schema without a resolve function defined.
add_action( 'graphql_register_types', function() {
register_graphql_field( 'RootQuery', 'hello', [
'type' => 'String',
]);
} );
This will add a hello
field to the RootQuery in the Schema and will allow this query to be executed:
{
hello
}
However, if we were to execute this, the results would be a null
value for the hello field, like so:
{
"data": {
"hello": null
}
}
This is because we registered the field to the Schema, but did not define a resolver.
Registering a field with a resolver:
Below is the same example as above, but with a resolve function included.
add_action( 'graphql_register_types', function() {
register_graphql_field( 'RootQuery', 'hello', [
'type' => 'String',
'resolve' => function() {
return 'world';
},
]);
} );
Here we defined a resolve
function and have it return the string world
. So now, if the above query were executed again, the results would be:
{
"data": {
"hello": "world"
}
}
In most cases, fields in a GraphQL schema will not simply resolve with a hard-coded string, like the above example. Resolvers often need more information to properly resolve.
In GraphQL, all resolve functions get passed 4 function arguments which the resolve function can use to during execution:
Post
type, for example, it would be passed an instance of a Post (\\WPGraphQL\\Post\\Model
)To better understand how these function arguments work, let’s add an argument to the hello
field to accept a name as input:
add_action( 'graphql_register_types', function() {
register_graphql_field( 'RootQuery', 'hello', [
'type' => 'String',
'args' => [
'name' => [
'type' => 'String',
'description' => __( 'Enter your name so GraphQL can say hello to you', 'your-textdomain' ),
]
],
'resolve' => function( $root, $args, $context, $info ) {
// if the name argument was input in the query, return it
return ( isset( $args['name'] ) ? $args['name'] : 'world';
},
]);
} );
Here we added an argument named name
that accepts a string.
In the resolve function, we added the 4 function arguments to the function call, and we check to see if the name
argument was passed through on the field, and if so, we return the value for the resolver, and default to world
if no value was input.
Now we could query like so:
{
hello( name: "Pam" )
}
And we would get the following result:
{
"data": {
"hello": "Pam"
}
}
You may find yourself in a situation where you need to override an existing resolver. There are many ways to accomplish this. Let’s look at some examples:
During GraphQL execution, the graphql_pre_resolve_field
filter executes prior to the default field resolution. If this filter returns a value, it will return the value and skip executing the default resolver.
This can be used like so:
add_filter( 'graphql_pre_resolve_field', function( $default, $source, $args, $context, $info, $type_name, $field_key, $field, $field_resolver ) {
if ( 'rootquery' === strtolower( $type_name ) && 'hello' === $field_key ) {
return 'custom value';
}
return $default;
}, 10, 9 );
This first checks if the filter is being applied to the hello
field on the RootQuery
type, and if it is it returns a string of “custom value”.
The same query for the hello
field would now return the following:
{
"data": {
"hello": "custom value"
}
}
NOTE: We used
strtolower()
to convert the type name to lowercase because behind the scenes WPGraphQL converts type names to lowercase strings, so it’s always safest to check type names using all lowercase characters.
Learn more about the graphql_pre_resolve_field filter.
This filter is similar to above, but the difference is that this filter runs after default execution of the resolve field has already run.
Let’s say we wanted to prefix the results of the hello
field with something custom.
We could do that like so:
add_filter( 'graphql_resolve_field', function( $result, $source, $args, $context, $info, $type_name, $field_key, $field, $field_resolver ) {
if ( 'rootquery' === strtolower( $type_name ) && 'hello' === $field_key ) {
return 'some prefix ' . $result;
}
return $result;
}, 10, 9 );
Now executing the following query:
{
hello( name: "Pam" )
}
Would return the following, with our prefix before the existing results of the query:
{
"data": {
"hello": "some prefix Pam"
}
}
Learn more about the graphql_resolve_field filter.
Below is an example of replacing the field’s resolve function altogether.
Let’s say we wanted the hello
field to always return the string “goodbye”, no matter what.
We could replace the resolve function for the field like so:
add_filter( 'graphql_RootQuery_fields', function( $fields ) {
// First, make sure there's actually a "hello" field registered to the RootQuery Type
if ( isset( $fields['hello'] ) ) {
// Override the resolve function completely
$fields['hello']['resolve'] = function() {
return 'goodbye';
}
}
return $fields;
}, 10, 1 );
Now the same query for the hello field (with or without an input argument supplied to the query) would return the following:
{
"data": {
"hello": "goodbye"
}
}