GraphQL in Rails 5

Introduction

We will first write a simple Hello GraphQL World program using graphql gem. We will then see an ActiveRecord example, a model with a self-reflexive association: person has many friends relationship. Working with GraphQL involves three steps:

  • Define some types
  • Connect them to a schema
  • Execute queries with your schema

Hello GraphQL in Ruby

The simple Hello GraphQL stolen from the graphql gem home page:

# Define some types
QueryType = GraphQL::ObjectType.define do
  name 'Query'
  field :hello do
    type types.String
    resolve -> (obj, args, ctx) { 'Hello GraphQL' }
  end
end

# Connect them to a schema
Schema = GraphQL::Schema.define do
  query QueryType
end

# Execute queries with your schema
puts Schema.execute('{ hello }')

You can run this from a IRB or rails console. You must have installed the graphql gem. We first define a query type, QueryType. We then connect them to a schema, by calling the GraphQL::Schema.define method. We then execute the query with our schema by calling the execute class method in Schema class.

GraphQL with ActiveRecord in Rails 5

Initial Setup

Define the following gems in the Gemfile and run bundle install.

gem "graphql"
gem "graphiql-rails"

Mount the GraphiQL Rails engine in routes.rb:

post '/graphql', to: 'graphql#query'
if Rails.env.development?
  mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
end

Create the model and the migration.

rails g model people first_name last_name email username
rails g migration create_friendships person:references friend:references

Add data to play with in seeds.rb:

me = Person.create(first_name: 'Steven', last_name: 'Luscher', email: 'steveluscher@fb.com', username: 'steveluscher')
dhh = Person.create(first_name: 'David', last_name: 'Heinemeier Hansson', email: 'dhh@37signals.com', username: 'dhh')
ezra = Person.create(first_name: 'Ezra', last_name: 'Zygmuntowicz', email: 'ezra@merbivore.com', username: 'ezra')
matz = Person.create(first_name: 'Yukihiro', last_name: 'Matsumoto', email: 'matz@heroku.com', username: 'matz')
me.friends << [matz]
dhh.friends << [ezra, matz]
ezra.friends << [dhh, matz]
matz.friends << [me, ezra, dhh]

Define the friendships relationship in the Person model.

class Person < ApplicationRecord
  has_and_belongs_to_many :friends,
    class_name: 'Person',
    join_table: :friendships,
    foreign_key: :person_id,
    association_foreign_key: :friend_id  
end

Run the migration and seed the database. Create app/serializers/person_serializer.rb:

class PersonSerializer < ActiveModel::Serializer
  attributes :id, :first_name, :last_name, :email, :username, :friends
  def id
    object.id.to_s
  end
  def friends
    object.friend_ids.map do |id|
      Rails.application.routes.url_helpers.person_path(id)
    end
  end
end

Create the controller.

rails g controller people 

Render json in the controller as follows:

class PeopleController < ApplicationController
  def index
    render json: Person.all
  end
  def show
    render json: Person.find(params[:id])
  end
end

This controller is just to see the JSON in the browser, not related to GraphQL. Define the people resource in the routes.rb.

resources :people, :only => [:index, :show]

You can now see the JSON output of the data by browsing to localhost:3000/people to view all records and localhost:3000/people/1 to view a specific record. This shows all the attributes. Beauty of GraphQL is you need only one endpoint to retrieve only the data you need. Let's now use GraphQL.

Step 1

Create schema.rb in model directory to define some types:

PersonType = GraphQL::ObjectType.define do
  name 'Person'
  description 'Somebody to lean on'

  field :id, !types.ID
  field :firstName, !types.String, property: :first_name
  field :lastName, !types.String, property: :last_name
  field :email, !types.String, 'Like a phone number, but spammier'
  field :username, !types.String, 'Use this to log in to your computer'
  field :friends, -> { types[PersonType] }, 'Some people to lean on'
  field :fullName do
    type !types.String
    description 'Every name, all at once'
    resolve -> (obj, args, ctx) { "#{obj.first_name} #{obj.last_name}" }
  end
end

QueryType = GraphQL::ObjectType.define do
  name 'Query'
  description 'The root of all queries'

  field :allPeople do
    type types[PersonType]
    description 'Everyone in the Universe'
    resolve -> (obj, args, ctx) { Person.all }
  end
  field :person do
    type PersonType
    description 'The person associated with a given ID'
    argument :id, !types.ID
    resolve -> (obj, args, ctx) { Person.find(args[:id]) }
  end
end

Step 2

In schema.rb, connect them to a schema:

Schema = GraphQL::Schema.define do
  query QueryType
end

Step 3

Execute queries with your schema in the controller.

class GraphqlController < ApplicationController
  skip_before_filter :verify_authenticity_token

  def query
    # 3. Execute queries with your schema
    result = Schema.execute(params[:query], variables: params[:variables])
    render json: result
  end
end

You can now go to http://localhost:3000/graphiql on your browser to send the graphql queries to the server. The browser will have autocomplete to help you create the queries to send to the server. You will see the JSON response from the server, served by GraphqlController.

References

Getting Started with Rails GraphQL Relay
GraphQL Ruby Gem
GraphQL Gist
Zero to GraphQL in 30 Minutes
Steve Luscher Github Source


Related Articles

Watch this Article as Screencast

You can watch this as a screencast GraphQL in Rails 5


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.