Custom GraphQL Resolvers
Many GraphQL frameworks require you to write resolvers for each field. ElasticGraph works differently: it defines a full set of resolvers for you. Simply define your schema and index your data, and it provides the full GraphQL API.
However, the GraphQL API provided by ElasticGraph won’t meet every need. Custom resolvers allow you to augment the API provided by ElasticGraph with your own custom implementation. Here’s how to define a custom resolver.
Tip ElasticGraph provides friendly error messages. Instead of reading this guide, you can jump straight to step 3 and let the error messages guide you through the changes explained in steps 1 and 2.
Step 1: Define a Resolver Class
# in lib/roll_dice_resolver.rb
class RollDiceResolver
def initialize(elasticgraph_graphql:, config:)
@number_of_dice = config.fetch(:number_of_dice)
end
def resolve(field:, object:, args:, context:)
@number_of_dice
.times
.map { rand(args.fetch("sides")) + 1 }
.sum
end
end
Conventionally, resolvers are defined in lib
(and you’d put lib
on the Ruby $LOAD_PATH
).
As shown here, the resolver needs to define two methods:
initialize
- Defines constructor logic. Accepts two arguments:
-
elasticgraph_graphql
: theElasticGraph::GraphQL
instance, providing access to dependencies. -
config
: parameterized configuration values for your resolver.
-
resolve
- Defines the resolver logic. Accepts four arguments:
-
field
: theElasticGraph::GraphQL::Schema::Field
object representing the field being resolved. -
object
: the value returned by the resolver of the parent field. -
args
: arguments passed in the query. -
context
: a hash-like object provided by the GraphQL gem that is scoped to the execution of a single query.
-
Note
There’s a fifth optional argument: lookahead
. It is a GraphQL::Execution::Lookahead
object
which allows you to inspect the child field selections. However, providing it imposes some measurable overhead, and query resolution will be
more performant if you omit it from your resolve
definition.
In this case, our RollDiceResolver
simulates the rolling of the configured number_of_dice
, each of which has a number of sides
provided as a query argument.
Step 2: Register the Resolver
require(require_path = "roll_dice_resolver")
schema.register_graphql_resolver :roll_dice,
RollDiceResolver,
defined_at: require_path,
number_of_dice: 2
Custom resolvers must be registered with ElasticGraph in the schema definition, using the register_graphql_resolver
API.
Any arguments provided after defined_at:
get recorded as resolver config, which will later be passed to the resolver’s initialize
method.
In this case, we’ve registered the resolver to roll two dice.
Step 3: Assign the Resolver to a Field
schema.on_root_query_type do |t|
t.field "rollDice", "Int" do |f|
f.argument "sides", "Int" do |a|
a.default 6
end
f.resolver = :roll_dice
end
end
Here we’ve defined a field on Query
using on_root_query_type
.
We’ve assigned the :roll_dice
resolver to our custom field.
Step 4: Query the Custom Field
That’s all there is to it! With this custom resolver wired up, we can query the custom field:
query RollDice {
# Returns a random number between 1 and 12
roll6SidedDice: rollDice
# Returns a random number between 1 and 20
roll10SidedDice: rollDice(sides: 10)
}