Graphql-ruby: [1.9] Implementing a FindById helper when using GraphQL::Execution::Interpreter

Created on 10 Apr 2019  路  6Comments  路  Source: rmosolgo/graphql-ruby

I currently have a convenience helper class called FindById that allows me to quickly implement finder queries. This is implemented as a GraphQL::Function. For example:

field :my_field, function: Helper::FindById.new(MyType)

Since GraphQL::Functionis not supported when using GraphQL::Execution::Interpreter, what is the recommended way to implement this kind of helper? Should I implement a subclass of Field?

All 6 comments

Resolver is the replacement for Function

How do I parameterize a Resolver? function: takes an instance of the GraphQL::Function subclass. In my example, I pass the model class so I can call Model.find. But Resolver takes the Resolver class. E.g.:

field :my_field, resolver: Resolvers::FindByIdResolver

Good question... you can only pass in the class, not an instance of it I'm pretty sure. And Resolver doesn't have access to the field itself so you can't store metadata on it or anything.

As the guide above mentions, if you use this frequently enough then maybe just make a new class method which defines the field?

Yes. I guess sort of "inverting" this so that the convenience method programmatically generates a field with the appropriate field method that does the find_by_id would be a way to go. I'll give this a try. However, my point that Resolver is not really a direct replacement for GraphQL::Function stands.

For the record, below is what I implemented in my BaseObject class. So now in my Query class, I only need to do:

find_by_id_field :my_field_name, SomeGraphQLType, SomeModelClass

to declare a finder query. Thanks for the help @swalkinshaw!


        def self.find_by_id_field(field_name, type, model_class)
          field field_name, type, null: true do
            argument :id, ID, required: true
          end

          define_method field_name.to_sym do |**args|
            id = args[:id]
            m = model_class.find_by_id(id)

            raise ::GraphQL::ExecutionError.new("Couldn't find #{model_class.name} with 'id'=#{id}") unless m
            m
          end
        end

:+1: Seems like a good solution!

Another way to "parameterize" resolvers is to build subclasses on the fly, with class methods like:

class FindById < GraphQL::Schema::Resolver 
  class << self 
    attr_accessor :model_class 

    # @param model_class [Class] An ActiveRecord class 
    # @return [Class] A resolver which looks up members of `model_class` 
    def for_model(model_class) 
      Class.new(self) { self.model_class = model_class } 
    end 
  end 

  argument :id, ID, required: true 

  def resolve(id:)
    self.class.model_class.find(id)
  end  
end 
Was this page helpful?
0 / 5 - 0 ratings