You are here

How to Implement a GraphQL API in Rails

GraphQL was created by Facebook to solve nagging issues with RESTful APIs like having to make multiple roundtrips to the server to fetch required data. Tech titans like GitHub and Shopify are already using it in production but many businesses have yet to take the leap. Leigh Halliday over at Codeship will show you how to get going with the GraphQL API in Rails so you can start avoiding RESTful API headaches too. 

Getting Started

To start with, you’ll create a new Rails installation with the command: rails new landbnb --database=postgresql --skip-test. This example app will use three models: rental, a property to rent; user, the rental owner or the person who rents; and booking, a user staying at a rental for a certain period of time. You don’t need to worry about the data migrations or the definitions of these models too much. Simply take a look at the relevant files in Leigh’s GitHub repo if you’re curious. 

Installing GraphQL

This part is easy. Simply add the graphQL gm to your Gemfile and run ‘rails generate graphql:install’. This creates a new app/graphql folder where you’ll be working. There’s also a new controller with a new route in our Rails app; although we won’t need to worry about routing or controllers so much in this tutorial. Leigh strongly recommends you comment out the mutation line in app/graphql/landbnb_schema.rb until you have actually built some mutations since this can lead to errors. 


Let’s start by creating an initial query to fetch all rentals.

# app/graphql/types/query_type.rb
Types::QueryType = GraphQL::ObjectType.define do
  name "Query"

  field :rentals, !types[Types::RentalType] do
    resolve -> (obj, args, ctx) {

Now that you’ve define a rentals field, you can perform a query with it like this one:

query {
  rentals {

This will give you the ids of all the rentals. The rentals field in the code has two parts: a type and a resolver. The type is the type of data returned while a resolver just tells the server how to add data to the rentals field. 

Now you need to define what the Types::RentalType is:

# app/graphql/types/rental_type.rb
Types::RentalType = GraphQL::ObjectType.define do
  name 'Rental'

  field :id, !types.ID
  field :rental_type, !types.String
  field :accommodates, !types.Int
  # ... other fields ...
  field :postal_code, types.String

  field :owner, Types::UserType do
    resolve -> (obj, args, ctx) { obj.user }
  field :bookings, !types[Types::BookingType]

This essentially serializes an instance of the Rental model, defining which fields can be queried and what their types are.

You’ll notice there’s no owner field associated with the model. The resolver will take care of this. 

To test this out, run this query with http://localhost:3000/graphiql in the browser and using the GraphQL tool.

Queries With Arguments

Of course, normally we’ll want to provide some parameters with our queries. So let’s see how to do that. For example, maybe we want to limit the number of rentals returned. You can do this in the rentals field like so:

# app/graphql/types/query_type.rb
Types::QueryType = GraphQL::ObjectType.define do
  name "Query"

  field :rentals, !types[Types::RentalType] do
    argument :limit, types.Int, default_value: 20, prepare: -> (limit) { [limit, 30].min }
    resolve -> (obj, args, ctx) {
      Rental.limit(args[:limit]).order(id: :desc)

Here there’s a limit argument for the rental field which must be an integer. There’s a default value there too. 

Modifying Data

Of course, we don’t just want to fetch data. We want to modify it. Here you’ll create a mutation to allow a user to sign in. The query looks like this:

mutation {
  signInUser(email: {email: "test6@email", password: "secret"}) {
    user {

The root type is mutation and we’ve specified that we want a JWT token in return. For this to work, you need to uncomment the mutation line the db schema file above.
Then add a signInUser field to the mutation file:

# app/graphql/types/mutation_type.rb
Types::MutationType = GraphQL::ObjectType.define do
  name "Mutation"

  field :signInUser, function:

Now you need to create a separate file to handle this mutation.

# app/graphql/mutations/sign_in_user.rb
class Mutations::SignInUser < GraphQL::Function
  # define the arguments this field will receive
  argument :email, !Types::AuthProviderEmailInput

  # define what this field will return
  type Types::AuthenticateType

  # resolve the field's response
  def call(obj, args, ctx)
    input = args[:email]
    return unless input

    user = User.find_by(email: input[:email])
    return unless user
    return unless user.authenticate(input[:password]){
      token: AuthToken.token(user),
      user: user

The AuthToken class is in the models folder and it uses the json_web_token gem.

# app/models/auth_token.rb
class AuthToken
  def self.key

  def self.token(user)
    payload = {user_id:}
    JsonWebToken.sign(payload, key: key)

  def self.verify(token)
    result = JsonWebToken.verify(token, key: key)
    return nil if result[:error]
    User.find_by(id: result[:ok][:user_id])


Finally, you want to authenticate the user, checking the JWT token in the header every time the user makes a subsequent request. In GraphQL, you define headers sent with each request in the config/initializers/graphiql.rb file.

if Rails.env.development?
  GraphiQL::Rails.config.headers['Authorization'] = -> (_ctx) {
    "bearer #{ENV['JWT_TOKEN']}"

Then you need to modify the controller to pass the current_user as context to the GraphQL code.

# app/controllers/graphql_controller.rb
def execute
  # ...
  context = {
    current_user: current_user


def current_user
  return nil if request.headers['Authorization'].blank?
  token = request.headers['Authorization'].split(' ').last
  return nil if token.blank?

You can now get the current user with the authorization token. We’ll show this by adding a bookRental mutation file to the mutations folder that looks like this:

# app/graphql/mutations/book_rental.rb
class Mutations::BookRental < GraphQL::Function
  # define the required input arguments for this mutation
  argument :rental_id, !types.Int
  argument :start_date, !types.String
  argument :stop_date, !types.String
  argument :guests, !types.Int

  # define what the return type will be
  type Types::BookingType

  # resolve the field, perfoming the mutation and its response
  def call(obj, args, ctx)
    # Raise an exception if no user is present
    if ctx[:current_user].blank?
      raise"Authentication required")

    rental = Rental.find(args[:rental_id])

    booking = rental.bookings.create!(
      user: ctx[:current_user],
      start_date: args[:start_date],
      stop_date: args[:stop_date],
      guests: args[:guests]

  rescue ActiveRecord::RecordNotFound => e"No Rental with ID #{args[:rental_id]} found.")
  rescue ActiveRecord::RecordInvalid => e"Invalid input: #{e.record.errors.full_messages.join(', ')}")

Among other things, this will handle errors where the rental id is invalid or the booking dates are wrong, for example.


So, now you’ve defined queries, mutations and different types you can write queries with parameters and authenticate users with JWTs. The next step is to create your own GraphQL-based API. 


Be sure to read the next API Design article: Why Your API Documentation Shouldn't Rely on a Tool

Original Article

How to Implement a GraphQL API in Rails




Thank you for giving a brief explanation GRAPHQl on Rails. I was able to understand the concepts. You had explained each and every point so clearly. Demo code for authorization token halped me a lot.User request of queries are well established.