How to query GraphQL APIs with Ruby

Posted . Visible to the public.

While most Rails Apps are tied to at least one external REST API, machine-to-machine communication via GraphQL is less commonly seen. In this card, I'd like to give a quick intro on how to query a given GraphQL API - without adding any additional library to your existing app.

Core aspects of GraphQL

Interacting with GraphQL feels a bit like querying a local database. You are submitting queries to fetch data in a given structure (like SELECT in SQL) or mutations to alter the database (similar to POST/PUT/DELETE in REST). You can control how the returning data looks even when nesting multiple levels deep with so called declarative data fetching - and all requests are being sent to a single URL .

For a quick overview of such an API, you can look at the Linear API documentation Show archive.org snapshot and the corresponding API endpoint explorer Show archive.org snapshot .

Querying GraphQL with Ruby

There is a gem called graphql-client Show archive.org snapshot , but it's still marked as experimental. I found that it's not really required to add another library for this use case, any good network-client gem like the http Show archive.org snapshot or httpclient Show archive.org snapshot gems serve us just well most of the time.

The examples below use the http gem ("http.rb"), but should be easily migrateable to the other clients.

Reading data (Query)

Let's assume I'm interested in the list of all Linear projects I have access to and what their corresponding team slugs ("keys") are. In a first step, I have to look up the corresponding query, available filters and attributes Show archive.org snapshot in the API explorer. Translated into a slim Ruby class below, we can then query all Linear projects like so:

GraphqlClient.new.list_projects
# [{"id"=>"ed695c5a-05c1-47f5-970d-662e56f463f9", "name"=>"readacted", "teams"=>{"nodes"=>[{"key"=>"RD"}]}},...
class GraphqlClient
  class APIError < StandardError; end

  API_ENDPOINT = 'https://api.linear.app/graphql'.freeze

  def list_projects
    perform_query(<<~QUERY).dig(*%w[data projects nodes])
      query Projects  {
        projects(includeArchived: true) {
          nodes {
            id
            name
            teams {
              nodes {
                key
              }
            }
          }
        }
      }
    QUERY
  end

  private

  def perform_query(query, variables: nil)
    response = client.post(API_ENDPOINT, json: { query:, variables: })
    parsed_response = JSON.parse(response.body)
    raise APIError, parsed_response.dig(*%w[errors]) unless response.status.ok?

    parsed_response
  end

  def client
    headers = {
      'Content-Type' => 'application/json',
      'Authorization' => Rails.application.secrets.fetch(:linear_api_key),
    }
    HTTP.headers(headers)
  end
end

Writing data (Mutation)

Performing GraphQL mutations is equally simple. Here is another example, this time using the ProjectCreate Show archive.org snapshot endpoint and a hash variable called "$input":

GraphqlClient.new.create_project!(
  name: 'Project created via the API',
  teamIds: ['ef4879ff-6ae3-466b-9f0d-8ea861a31f38'],
)
class GraphqlClient
 # ..

  def create_project!(project_attributes)
    perform_query(<<~GRAPHQL, variables: { input: { **project_attributes } }).dig(*%w[data projectCreate project])
      mutation ProjectCreate($input: ProjectCreateInput!) {
        projectCreate(input: $input) {
          success
          project {
            id
            url
          }
        }
      }
    GRAPHQL
  end

  # ...
Michael Leimstädtner
Last edit
Michael Leimstädtner
License
Source code in this card is licensed under the MIT License.
Posted by Michael Leimstädtner to makandra dev (2024-08-26 11:27)