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?
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