⚠ New Class-Based API ⚠

This describes a new API in v1.8.0. Check the upgrade notes for more info.

Resolvers

A GraphQL::Schema::Resolver is a container for field signature and resolution logic. It can be attached to a field with the resolver: keyword:

# Use the resolver class to execute this field
field :pending_orders, resolver: PendingOrders

Under the hood, GraphQL::Schema::Mutation is a specialized subclass of Resolver.

First, ask yourself …

Do you really need a Resolver? Putting logic in a Resolver has some downsides:

Here are a few alternatives to consider:

field :recommended_items, [Types::Item], null: false
def recommended_items
  ItemRecommendation.new(user: context[:viewer]).items
end
# Generate a field which returns a filtered, sorted list of items
def self.items_field(name, override_options)
  # Prepare options
  default_field_options = { type: [Types::Item], null: false }
  field_options = default_field_options.merge(override_options)
  # Create the field
  field(name, field_options) do
    argument :order_by, Types::ItemOrder, required: false
    argument :category, Types::ItemCategory, required: false
    # Allow an override block to add more arguments
    yield if block_given?
  end
end

# Then use the generator to create a field:
items_field(:recommended_items) do
  argument :similar_to_product_id, ID, required: false
end
# Implement the field
def recommended_items
  # ...
end

As a matter of code organization, that class method could be put in a module and shared between different classes that need it.

module HasRecommendedItems
  def self.included(child_class)
    # attach the field here
    child_class.field(:recommended_items, [Types::Item], null: false)
  end

  # then implement the field
  def recommended_items
    # ...
  end
end

# Add the field to some objects:
class Types::User < BaseObject
  include HasRecommendedItems # adds the field
end

When do you really need a resolver?

So, if there are other, better options, why does Resolver exist? Here are a few specific advantages:

Using resolver

To add resolvers to your project, make a base class:

# app/graphql/resolvers/base.rb
module Resolvers
  class Base < GraphQL::Schema::Resolver
    # if you have a custom argument class, you can attach it:
    argument_class Arguments::Base
  end
end

Then, extend it as needed:

module Resolvers
  class RecommendedItems < Resolvers::Base
    type [Types::Item], null: false

    argument :order_by, Types::ItemOrder, required: false
    argument :category, Types::ItemCategory, required: false

    def resolve(order_by: nil, category: nil)
      # call your application logic here:
      recommendations = ItemRecommendation.new(
        viewer: context[:viewer],
        recommended_for: object,
        order_by: order_by,
        category: category,
      )
      # return the list of items
      recommendations.items
    end
  end
end

And attach it to your field:

class Types::User < Types::BaseObject
  field :recommended_items,
    resolver: Resolvers::RecommendedItems,
    description: "Items this user might like"
end

Since the Resolver lifecycle is managed by the GraphQL runtime, the best way to test it is to execute GraphQL queries and check the results.