Build a Rails 6 API Featuring Graphql
Tags: rails, graphql, ruby
Published: 2020-10-28 03:36:24

# Table of Contents

* [Initial Configuration](#initial-configuration)
* [Generate a New Rails API](#generate-a-new-rails-api)
* [Gem Additions](#gem-additions)
* [`rack-cors` Gem Configuration](#)
* [First Migration](#first-migration)
* [New Graphql Generator](#new-graphql-generator)
* [Schema, Types](#schema-types)
* [Resources](#resources)

In this blog, I'll be covering how to create an initial Rails backend API utilizing [Graphql](https://graphql.org/). 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: 

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

> 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:

```ruby
config.hosts << "localhost"
```

> 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](https://guides.rubyonrails.org/configuring.html#configuring-middleware).

### 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`:

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

User.create(
  name: "Ethan Gustafson",
  username: "GoodGuyGuf",
  password: "123"
)
```

- 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](https://github.com/rmosolgo/graphiql-rails/issues/13#issuecomment-640366886).

Follow the [`graphiql-rails`](https://github.com/rmosolgo/graphiql-rails) guide normally. 

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

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

In `config/application.rb`, uncomment:

```ruby
require "sprockets/railtie"
```

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:

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

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:

```ruby
rails generate graphql:install
```

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](https://graphql-ruby.org/getting_started#build-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.

```ruby
# 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
```

The `User` Type:

```ruby
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
```

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.

```ruby
# 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
```

**Results**:

![Graphiql Result](https://drive.google.com/uc?export=view&id=1CofjtyEaU-OqedOwSPTOaZ2JxZma7scv)

## **Resources:**

- [Graphql](https://graphql.org/)
- [Getting Started With The Graphql Gem](https://graphql-ruby.org/getting_started)
- [Graphql Gem Guides](https://graphql-ruby.org/guides)
- [Graphiql-Rails Gem](https://github.com/rmosolgo/graphiql-rails)
- [Rack-Cors Gem](https://github.com/cyu/rack-cors)