Graphql APIs with Rails 7


Before we get started the tools we'll use to build our API.

  • Ruby 3.1 or greater

  • Rails 7 or greater

  • PostgreSQL

  • The graphql-ruby gem. (Documentation)


Creating The Rails API

rails new PROJECT_NAME --api --database=postgresql
rails db:create
rails db:migrate

Installing the gems we need

gem 'rack-cors'
gem 'graphql'
group :development do 
  gem 'graphiql-rails' # Adds graphiql so we can test queries
  gem 'faker' # We'll use this to generate test data
end

Once these items are added to the .gemfile we need to install them by running the following in the terminal. After the gems have been installed, we need to run the generator to set up the graphql gem. This will create the base structure needed for our graphql api.

bundle install
rails generate graphql:install

After running the generator you should see a new folder in your app directory called "graphql". Additionally, the routes file(found at "config/routes.rb") should now contain a route for the graphql endpoint that will handle all of the queries and mutations we need for our blog. This is one of the biggest differences between a traditional rails api and one using graphql, in a traditional rails api we would need to create a route for each of our endpoints.

Rails.application.routes.draw do
  post "/graphql", to: "graphql#execute"
end

We also need to go to "cors.rb" and uncomment the following code and update the "origins" to be localhost 3000. We'll run our rails api on port 3001.

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "http://localhost:3000"

    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

Next, let's set up graphiql so we can test our queries.

Rails.application.routes.draw do
  if Rails.env.development?
    mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "graphql#execute"
  end
  post "/graphql", to: "graphql#execute"
end

Before we are able to access graphiql there are a few more tweaks we need to make. First, we need to add the two lines with comments next to them to config/application.rb.

require_relative "boot"

require "rails/all"
require "sprockets/railtie" # add this line to enable sprockets. Required to use graphiql.

Bundler.require(*Rails.groups)
module RailsGql
  class Application < Rails::Application
    config.load_defaults 7.0
    config.api_only = true
    # add these lines to enable sessions. Required to use graphiql.
    config.session_store :cookie_store, key: '_interslice_session'
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use config.session_store, config.session_options 
  end
end

Next, we need to add an `assets` directory inside of `app`. After creating the `assets` directory, create another directory called `config` and add a file to that directory called `manifest.js`. Then include the following two lines, they appear to be commented out, but this is the syntax that sprockets expects.

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

That's all we need to get graphiql configured. We can test it out by running `rails s` to start the rails server, then navigating to `localhost:3000/graphiql`. The graphql gem adds a test query for us when running the generator, give it a test run to make sure it's working.

Next, let's add the model that we will need for the blog posts. This can be done manually but we'll use a rails generator to save some time. When creating a model, be sure to capitalize the name and use the singular form of the word (example: "User" not "Users"). Rails will handle pluralizing the names of the models if/when it's relevant.

rails g model Post title:string slug:string category:string body:text

And just like that, we've created a model and a database migration for our posts. To run the migration simply run 'rails db:migrate' again and it will create the table and its corresponding columns in the database. Now, let's use faker to add some fixture data for use to play with. To do this open the 'seeds.rb' file that is found in the 'db' folder and add the following.

5.times do
  Post.create(title: Faker::Lorem.sentence(word_count: 3), slug: Faker::Internet.slug, body: Faker::Lorem::paragraph(sentence_count: 3))
end

Then run.

rails db:seed
Next we need to set up the graphql object/type we'll need for our posts. To do this we'll reach for another generator provided by the graphql gem.

rails g graphql:object post

Lastly, we need to add some queries to retrieve our post data. Update 'query_type.rb' to include the following fields and methods, we can remove the test field as well.

module Types
  class QueryType < Types::BaseObject
    # Add `node(id: ID!) and `nodes(ids: [ID!]!)`
    include GraphQL::Types::Relay::HasNodeField
    include GraphQL::Types::Relay::HasNodesField
    field :posts, [Types::PostType], null: false
    field :post, Types::PostType, null: false do
      argument :id, ID, required: true
    end
    # Add root-level fields here.
    # They will be entry points for queries on your schema.
    def posts
      Post.all
    end
    def post(id:)
      Post.find(id)
    end
  end
end

Now we're ready to start setting up the front end!