Introducing SearchObject GraphQL Plugin
When I started using GraphQL, I immediately saw, that SearchObject would be a perfect fit for search resolvers.
Having a GraphQL query to fetch the first 10 most recent published news posts would look something like this:
query {
posts(first: 10, categoryName: "News", order: "RECENT", published: true) {
id
title
body
author {
id
name
}
}
}
And it would have a corresponding SearchObject:
class Resolvers::PostSearch
include SearchObject.module
scope { Post.all }
option :categoryName, with: :apply_category_name_filter
option :published, with: :apply_published_filter
option :order, enum: %i(RECENT VIEWS LIKES)
# ... definitions of the option methods
end
So clean. ☀️
But then, PostSearch
have to be connected with GraphQL Ruby gem:
PostOrderEnum = GraphQL::EnumType.define do
name 'PostOrder'
value 'RECENT'
value 'VIEWS'
value 'LIKES'
end
Types::QueryType = GraphQL::ObjectType.define do
name 'Query'
field :posts, types[Types::PostType] do
argument :categoryName, types.String
argument :published, types.Boolean
argument :order, PostOrderEnum
resolve ->(_obj, args, _ctx) { Resolvers::PostSearch.results(filters: args.to_h) }
end
end
That isn't so bad. ?
But then, thinking about how can this code can change in the future:
- adding/removing options would involve going to both files
- adding a new order option would mean searching for the
PostOrderEnum
and manually sync it withPostSearch
enum - reusing
PostSearch
in other types, for queries like:query { user(id: 1) { posts(published: true) } }
- requires copy and paste argument/type definitions
- which makes updating the resolver even harder
Yikes! ? ?
This is where SearchObject::Plugin::GraphQL comes in. It puts type definitions and the resolver itself:
class Resolvers::PostSearch
# include the the plugin
include SearchObject.module(:graphql)
# documentation and type about this resolver
# can be provided into the resolver itself
type types[Types::PostType]
description 'Lists posts'
# enums or other types can also be nested
OrderEnum = GraphQL::EnumType.define do
name 'PostOrder'
value 'RECENT'
value 'VIEWS'
value 'LIKES'
end
scope { Post.all }
# options just need to have a their type specifed.
option :categoryName, type: types.String, with: :apply_category_name_filter
option :published, type: types.Boolean, with: :apply_published_filter
# enums are automatically handled
option :order, type: OrderEnum
# ... definitions of the option methods
end
Then PostSearch
can be used just as GraphQL::Function:
Types::QueryType = GraphQL::ObjectType.define do
name 'Query'
field :posts, function: Resolvers::PostSearch
end
Now, changing filter options requires changing only a single file. PostSearch
can be reused in other types, by just adding function: Resolvers::PostSearch
.
For more information check SearchObject::Plugin::GraphQL example.