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
# ...