GraphQL Performance Testing (Part 3): Up and Running with Rails GraphQL

Now that our Rails models are in order we can finally get to creating our first GraphQL API.

Step 1: Set up the gem

graphql-ruby has great documentation for doing setup. this is a summary

1. Generate the installation

$ rails g graphql:install

2. Generate our models

$ rails g graphql:object  User first_name:String last_name:String email:String invoices:\\[Invoice\\]

$ rails g graphql:object InvoiceItem amount:Int description:String price:String price_cents:Int invoice:Invoice

$ rails g graphql:object Invoice number:String date:String total_cents:Int total:String invoice_items:\\[InvoiceItem\\] creator:User

This should create our initial GraphQL types

Step 2. Installing GraphiQL

It's almost time to start up the server, but we need to test types first.

We can install the graphiql gem to get an easy way to interact with and iterate over our schema as we create it.

Since we started rails in api mode, we need to make a few changes to get everything working. This issue has more details if these instructions don't work for you.

  • Check that we have the gem
# Gemfile

gem \"graphiql-rails\"
  
  # we need sprockets now to load css on js
  
  • Add the routes
# config/routes.rb

if Rails.env.development?
    mount GraphiQL::Rails::Engine, at: \"/graphiql\", graphql_path: \"/graphql\"
end
  • Add in the sprockets railtie
# config/application.rb

require 'sprockets/railtie'
  • create a manifest.js file
/* app/assets/config/manifest.js */

//= link graphiql/rails/application.css
//= link graphiql/rails/application.js

This should ensure we have access to graphiql, but only in a development setting.

try starting the rails server and seeing if it works.

cd rails_api
rails server

then open localhost:3000/graphiql

Step 3. Setting up the Query Type

Now that rails is back up to speed. We can create the QueryType.

The QueryType determines the root of our api. A good api design should determine which objects should be present at the root and which should only be access through their relationship to other objects. In a more advanced app we could show or hide fields based on user permissions or limit the results returned to those a user is able to access.

For our test, we will create a way to select a list of invoices and a single invoice using its ID.

Creating a list of Invoices

# app/graphql/types/query_type.rb

  field :invoices, [Types::InvoiceType], null: false, description: 'A list of invoices'

def invoices
  #  
  Invoice.all.limit(5)
end

By default the rails class will call the method of the same name. When using a regular rails object, this means calling a method on the associated object or trying to access a hash key with that name.

Methods can be overridden, or custom methods can be added.

Here we'll just pull the first five invoices to make sure everything is working properly.

Just refresh graphiql and see the updated api.

Something like the following should pull up a list of invoices, creators, and invoice items without error.

query {
    invoices {
      date
      number
  
      creator {
        lastName
        firstName
        email
      }

    invoiceItems {
        amount
        priceCents
      }
  } 
}

Note: the graphql-ruby gem will follow naming conventions both ruby and javascript, so methods in snake_case in ruby will automatically translated to camelCase in graphql and javascript.

Updating the field

A listing of all fields is great. But, it would be better to tell the system how many records we would like returned. This can easily be done by adding an argument to the field

# app/graphql/types/query_type.rb

  field :invoices, [Types::InvoiceType], null: false, description: 'A list of invoices' do
      argument :count, Integer, required: false, default_value: 5
  end

  def invoices(count:)
    Invoice.all.limit(count)
  end

Arguments appear as keyword arguments in the resolver method. By adding a default value, the initial response will remain the same but now we can specify the maximum number of returned records.

query {
    invoices(count: 50) {
      date
      number
  
      creator {
        lastName
        firstName
        email
      }

    invoiceItems {
        amount
        priceCentsnn
      }
  } 
}

Since we have already specified the type of the argument, graphql-ruby also takes care of converting user input to an Integer for us.

query {
    invoices(count: \"Muffins\") {
      date
      number
     ...
  }

Adding an incorrect value will return a helpful error instead of being passed to the application code.

Add a way to lookup a single Invoice

In order to look up a single invoice, we can add another field to the QueryType. We'll keep to the rails naming conventions, so we end up with the plural invoices for a list and the singular invoice when looking up a single record.

First, we should expose the id attribute on the InvoiceType

# app/graphql/types/invoice_type.rb

field :id, ID, null: false
query {
  invoice(id: 12345) {
    date
    number
   ...
}