loading...
Cover image for Build a Rails 6 API Featuring Graphql

Build a Rails 6 API Featuring Graphql

ethanmgustafson profile image Ethan Gustafson ・4 min read

Table of Contents

In this blog, I'll be covering how to create an initial Rails backend API utilizing Graphql. If you don't know Graphql, if you're familiar with it or just need a refresher, this blog is for you.

Initial Configuration

Generate a New Rails API

Let's generate a new API-only application. I won't be using most of the gems that come with a new rails generation, so the command I will use with numerous options is:

rails new vacationme_backend --api --database=postgresql --skip-action-mailer --skip-action-mailbox --skip-action-cable

Gem Additions

  • Uncomment the rack-cors gem
  • Add gem 'graphql'
  • Add gem 'graphiql-rails'
  • Run bundle install

Unfortunately, the graphiql-rails hasn't been updated since January of 2019. There are many pull requests and issues stated, and quite a few direct you to how the application can begin working again. I will show you how you can do so further down.

rack-cors Configuration

In config/initializers/cors.rb, uncomment the following code:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :patch, :put]
  end
end
Enter fullscreen mode Exit fullscreen mode

For Rails, you'll need to add this middleware on application startup. A practical way to do this is with an initializer file. For example, the following will allow GET, POST, PATCH, or PUT requests from any origin on any resource:

We use insert_before to make sure Rack::Cors runs at the beginning of the stack to make sure it isn't interfered with by other middleware (see Rack::Cache note in Common Gotchas section). Basic setup examples for Rails 5 & Rails 6 can be found in the examples/ directory.

In config/application.rb, add:

config.hosts << "localhost"
Enter fullscreen mode Exit fullscreen mode

Rails 6 has support for blocking requests from unknown hosts, so origin domains will need to be added there as well.

A more detailed reason for this addition can be found here in the Ruby on Rails Guide.

First Migration

Graphql will be interacting with ActiveRecord. We will have to create our migrations and models as we normally would. I am using the bcrypt gem, so instead of a password field, it will be a password_digest field.

Run:

  • rails g model user name:string username:string password_digest:string
  • rails db:create
  • rails db:migrate

Remember to include has_secure_password in app/models/user.rb. Now let's write two seeds in db/seeds.rb:

User.create(
  name: "John Doe",
  username: "JohnDoe",
  password: "123"
)

User.create(
  name: "Ethan Gustafson",
  username: "GoodGuyGuf",
  password: "123"
)
Enter fullscreen mode Exit fullscreen mode
  • Run rails db:seed

Mounting The Engine

The graphiql-rails gem enables one route in your application where you can open a browser and develop using an IDE for graphql.

After navigating through the issues, I stumbled onto the best solution I could use without changing much of the code I currently have. It can be found here in this issue.

Follow the graphiql-rails guide normally.

# config/routes.rb
post "/graphql", to: "graphql#execute"
if Rails.env.development?
  mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "graphql#execute"
end
Enter fullscreen mode Exit fullscreen mode

You would navigate to "/graphiql" in your browser, and the path would still use the graphql#execute action.

In config/application.rb, uncomment:

require "sprockets/railtie"
Enter fullscreen mode Exit fullscreen mode

Sprockets is a Ruby library for compiling and serving web assets.

To create the last fix, run touch app/assets/config/manifest.js to create a manifest.js file. Put this inside of it:

//= link graphiql/rails/application.css
//= link graphiql/rails/application.js
Enter fullscreen mode Exit fullscreen mode

That's all. This is so the gem can link to the CSS and js it defined in its own files. Now we can begin building the types.

New Graphql Generator

Now that we have a user and a record, we can generate the graphql install. Run:

rails generate graphql:install
Enter fullscreen mode Exit fullscreen mode

This will create:

  • A graphql_controller
  • A graphql directory containing two subdirectories and one file:
    • mutations, which contains 1 file
    • types, which contains 10 files
    • project_name_schema.rb

To keep it simple I won't go into detail about the mutations directory.

Schema, Types

GraphQL cannot execute a query without a type system.

There are object, mutation, query, scalar, enumeration, interfaces, union, and input types.

Every GraphQL service defines a set of types which completely describe the set of possible data you can query on that service. Then, when queries come in, they are validated and executed against that schema.

Before building a schema, you have to define an entry point to your system, which is called the “query root”. The query root will be the query_type.rb file.

# app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    # Add root-level fields here.
    # They will be entry points for queries on your schema.

     # First describe the field signature:
    field :user, UserType, null: true do
      description "Find a User by ID"
      argument :id, ID, required: true
    end

    def user(id:)
      User.find(id)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

The User Type:

module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :username, String, null: false 
    field :password, String, null: false
  end
end
Enter fullscreen mode Exit fullscreen mode

Including the root query in the schema ensures that the application is now functional. Run rails s. Your application should be working normally, and you can run queries on the types above without a problem.

# app/graphql/vacationme_backend_schema.rb
class VacationmeBackendSchema < GraphQL::Schema
  mutation(Types::MutationType)
  query(Types::QueryType)

  # Opt in to the new runtime (default in future graphql-ruby versions)
  use GraphQL::Execution::Interpreter
  use GraphQL::Analysis::AST

  # Add built-in connections for pagination
  use GraphQL::Pagination::Connections
end
Enter fullscreen mode Exit fullscreen mode

Results:

Graphiql Result

Resources:

Discussion

pic
Editor guide