This is a series blog post cover above three topics of GraphQL:
- About GraphQL
- Building a basic API with Rails
- Some best practices
GraphQL is not a new thing, it already exist for a while, the reason I decide to investigate it is because Github released their GraphQL API alpha currently. We know Github has been a very good company for a long time, their RESTful API also becomes some kind of standard for developers, so it’s pretty interesting to see what’s new they’ve been released.
This topic is based on my MeetUp talk, to know more you can check my slide and example repo.
Alright, Let’s get started!
Say we have 2 types shares many fields, for example they all have such like id, updated_at, created_at basic ActiveRecord fields. How do we clean those types? One way to do it is use Interfaces. Let’s take a look:
Before refactoring, UserType and PostType looks like this:
# app/graph/types/user_type.rb
UserType = GraphQL::ObjectType.define do
name "User"
description "A user"
field :id, types.Int
field :email, types.String
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
end# app/graph/types/post_type.rb
PostType = GraphQL::ObjectType.define do
name "Post"
description "A post"
field :id, types.Int
field :title, types.String
field :content, types.String
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
endNow let’s create a active_record_interfaces.rb file and defines id, updated_at, created_at
# app/graph/types/active_record_interfaces.rb
ActiveRecordInterface = GraphQL::InterfaceType.define do
name "ActiveRecord"
description "Active Record Interface"
field :id, types.Int
field :updated_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.updated_at.to_i
}
end
field :created_at do
type types.Int
resolve -> (obj, args, ctx) {
obj.created_at.to_i
}
end
endThen our UserType and PostType can becomes much clearer:
# app/graph/types/user_type.rb
UserType = GraphQL::ObjectType.define do
interfaces [ActiveRecordInterface]
name "User"
description "A user"
field :email, types.String
end# app/graph/types/post_type.rb
PostType = GraphQL::ObjectType.define do
interfaces [ActiveRecordInterface]
name "Post"
description "A post"
field :title, types.String
field :content, types.String
endWe use resolve block to handle each fields’ behaviour, however it will becomes fragile if we add code logic into resolve block, and also it’s not easy to test. So the better way is to keep logic away from resolve block but stay at Service Object (or other patterns you want to use, the idea is abstract it).
Ex:
The code below
CreatePostMutation = GraphQL::Relay::Mutation.define do
# ...
resolve -> (object, inputs, ctx) {
post = ctx[:current_user].posts.create(title: inputs[:title], content: inputs[:content])
{
post: post
}
}
endCould be
CreatePostMutation = GraphQL::Relay::Mutation.define do
# ...
resolve -> (object, inputs, ctx) {
Graph::CreatePostService.new(inputs, ctx).perform!
}
endSo that in this case we only need to test Graph::CreatePostService, more testable and maintainable.
In graphql-ruby gem, there are couple built in module with Relay namespace, if you want your API looks like GitHub GraphQL API or just want to integrate it with Relay, it will saves you tons of time.
Check those modules below to get a taste:
- GraphQL::Relay::ConnectionType
- GraphQL::Relay::Node
- GraphQL::Relay::Edge
Those are the experience I learned, have more tips to share? Leave a comment here. Thanks!