Skip to content

Instantly share code, notes, and snippets.

@wayne5540
Created January 30, 2017 05:46
Show Gist options
  • Save wayne5540/a396634d61c351acc621b3ceba28e769 to your computer and use it in GitHub Desktop.
Save wayne5540/a396634d61c351acc621b3ceba28e769 to your computer and use it in GitHub Desktop.

# Building a GraphQL API in Rails - Part 3 - Best Practice

This is a series blog post cover above three topics of GraphQL:

  1. About GraphQL
  2. Building a basic API with Rails
  3. 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!

1. Interfaces

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
end

Now 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
end

Then 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
end

2. Service object

We 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
    }
  }
end

Could be

CreatePostMutation = GraphQL::Relay::Mutation.define do
  # ...

  resolve -> (object, inputs, ctx) {
    Graph::CreatePostService.new(inputs, ctx).perform!
  }
end

So that in this case we only need to test Graph::CreatePostService, more testable and maintainable.

3. Use Relay module

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment