Talking to the Github GraphQL endpoint with Rails 6
Tags: Ruby, Tutorial, GraphQL, Ruby on Rails, Rails

This story covers the following topics:

Adding a GraphQL client to your Rails application

Build a generic interface to talk with the Github GraphQL v4 API endpoint

Perform a Github search within your Rails application and return its results in a HTML view

Setting up our example search application

rails _6.0.3.4_ new --database=postgresql --skip-turbolinks --skip-test github_search && cd github_search bundle exec bin/setup

Talking to Github with a GraphQL client

We will use the Ruby GraphQL client graphql-client from Github.

bundle add 'graphql-client' -v 0.16.0

Since the client validates the request before submitting, we need to fetch a schema from our API endpoint. For Github this endpoint is https://api.github.com/graphql. Together with a personal access token from https://github.com/settings/tokens we can start implementing the client.

First add the personal access token to your encrypted credentials bundle exec rails credentials:edit :

github_personal_access_token: some-secret-token

Then create the client module config/initializers/github.rb :

require 'graphql/client'

require 'graphql/client/http'



module Github



SCHEMA_DIRECTORY_PATH = File.join(Rails.root, 'config', 'graphql').freeze

SCHEMA_PATH = File.join(SCHEMA_DIRECTORY_PATH, 'github_schema.json').freeze



HTTP = GraphQL::Client::HTTP.new('https://api.github.com/graphql') do

def headers(_context)

{

'Authorization' => "Bearer #{Rails.application.credentials.github_personal_access_token}",

'Accept' => 'application/vnd.github.v4+json'

}

end

end



unless File.exists?(SCHEMA_PATH)

FileUtils.mkdir_p(SCHEMA_DIRECTORY_PATH)

GraphQL::Client.dump_schema(HTTP, SCHEMA_PATH)

end



Schema = GraphQL::Client.load_schema(SCHEMA_PATH)



Client = GraphQL::Client.new(schema: Schema, execute: HTTP)



end

The graphql-client gem allows you to fetch the schema on each class load, but personally I would encourage you to submit the used schema to the source control. It also a best practice to specify the used API version, here v4 .

Finally we can add a Github search class in app/models/search.rb :

class Search



LIMIT = 100

SEARCH_REPOSITORY_META_QUERY = Github::Client.parse <<-'GRAPHQL'

query($query: String!, $limit: Int!) {

search(query: $query, type: REPOSITORY, first: $limit) {

edges {

node {

... on Repository {

nameWithOwner

homepageUrl

}

}

}

}

}

GRAPHQL



def execute(query)

results = Github::Client.query(

SEARCH_REPOSITORY_META_QUERY,

variables: { query: query, limit: LIMIT }

)



results.data.search.edges.map { |repository| repository.node.name_with_owner }

end



end

It builds a meta query with the two parameters $query and $limit for the REPOSITORY type. The query can be anything you can pass in into the Github search. In the end the results are mapped to the name_with_owner attribute.

Let’s see an example output:

% rails runner "puts Search.new.execute('active user:rails')"

rails/activeresource

rails/activestorage

rails/activejob

rails/activerecord-session_store

rails/strong_parameters

rails/activesupport-json_encoder

rails/activerecord-deprecated_finders

rails/activemodel-globalid

rails/activemodel-serializers-xml

It is also possible to play around with different queries in the GraphQL explorer.

The Github API explorer

Exposing the search to a controller action

Now we want to wrap around this search class a controller action with a related view.

Therefore create a route in config/routes.rb where a single index action is defined for our search.

Rails.application.routes.draw do

get :search, controller: :search, action: :index

end

The next step is to have a controller in app/controllers/search_controller.rb that passes the query param of the request to the search interface. Be careful with the user input you pass through: For this example application and in the context of a search you might not encounter any security issues. But keep in mind that all queries have the same privileges as the user of your personal access token.

class SearchController < ApplicationController

def index

@results = if search_params[:query].present?

Search.new.execute(search_params[:query])

else

[]

end

end



private



def search_params

search_params = params[:search]

search_params ? search_params.permit(:query) : {}

end

end

Finally the view in app/views/search/index.html.erb builds a form and renders the search results.

<h1>Search</h1>



<%= form_for :search, url: search_path, method: :get do |form| %>

<div>

<%= form.text_field :query, placeholder: 'Search...', value: params.dig('search', 'query') %>

</div>



<div>

<%= form.submit 'Search' %>

</div>

<% end %>



<table>

<tbody>

<% @results.each do |result| %>

<tr>

<td><%= result %></td>

</tr>

<% end %>

</tbody>

</table>

If you go to http://localhost:3000/search you should now be able to query the Github API.

Simple Github search interface

Outline

The GraphQL endpoint from Github offers many more resources you can integrate in your application. You might want to check out my side project wintermute to see how you paginate through results. Happy coding!