GraphQL with Ruby on Rails: Resolvers
The hardest part of the GraphQL learning journey, in my humble opinion, is to understand Resolvers. Last time resolvers are only briefly introduced,
Here is one way I find extremely useful to think about resolvers:
Resolver is the Controller
Now, I know I mentioned last time that the Controller role, in the context of the whole server, should be GraphQL itself. But remember how we compared the server to a restaurant? When the Rails router (counter) receives a request (order), it sends the request to the corresponding controller(cook). If the request is to be sent to GraphQL, however, the request would be transferred to the GraphQL server (an outsourced restaurant) for the following proceedings.
We can imagine that when the request goes to the GraphQL server, GraphQL would have its own set of cooks to fetch and manipulate the data (prepare the dishes). Resolvers are those cooks. When GraphQL finishes executing, the response will be sent back to the application server. The client would not know its request is handled by whom or how it is processed.
If there is only one thing for you to take away in this article, I hope this is it. Next, let us see how resolvers are used in a Ruby implementation.
Four Ways to Construct a Resolver (with GraphQL-Ruby gem)
1. Let it be (implicit resolver)
2. Same-name definition
3. Independent Ruby Class
4. GraphQL Resolver Class
A modified example of an object/type CategoryType
:
It is mostly the same as the working example snapshot at the beginning, except that I took out some unrelated complications and modified some modified some resolver methods for illustrating purpose. In this example each of the four fields is using one of the four resolver methods.
1. Let it be (Implicit Resolver)
This is the easiest. When a query sees a field, it automatically tries to resolve a field when the following conditions are met:
- The field is one of the scaler types.
- There is a method or hash key with the same name in the
object
argument.
This is what happens with the field key
in our example. It is a scalar type (String), and we assume the object respond to either object.key
or object[‘key']
(or else an error would occur), so the runtime will implicitly resolve the field without us providing a specific resolver.
2. Same-Name Definition
If we define a method that has the same name with the field, GraphQL will look into this method to resolve the field. In this example, name
method is the resolver for field name
. That is, the runtime will get the value of name
field by calling object.display_name
.
3. Independent Ruby Class
To be precise, this can actually be seen as an extension of the same-name definition resolver, except the logic is put inside a class/module of its own. The example here is the field icon_url
. The runtime still goes to the eponymous method first, which takes control to the QueryResolver
class
which takes it to the category
action of object. The return value of this action will be the object to be parsed against CategoryType
, the value of the second argument in the field definition.
The benefit of this structure is separation of concerns: the interface (schema) does not need to know the business logic (resolver), and
4. GraphQL Resolver Class
We need to make this clear that the document strongly advises AGAINST resorting to this method. Two main downsides: (1) [Coupling] It is coupled to GraphQL, which makes testing harder. (2) [Dependency] It inherits from the GraphQL-Ruby library, so if the library changes its code we might need to update ours accordingly.
But if we really have a good reason to use the resolver class, here is an example:
We will simply inherit GraphQL::Schema::Resolver
and implement resolver
method. GraphQL will take care of the rest.
Recap
We have learned that resolvers are how GraphQL knows where to fetch the data. A resolver’s job is like a controller’s in the GraphQL server, which is itself like a controller in the application. GraphQL-Ruby gives us a flexibility to build a resolver with one of the four choices. We will try not to use the last method unless absolutely necessary.
This is it. If you are still feeling unsatisfied, feel free to look into Shopify’s GraphQL tutorial–another well-written introduction to GraphQL with Ruby on Rails!
文章同步發表於 Medium。