At Agira, Technology Simplified, Innovation Delivered, and Empowering Business is what we are passionate about. We always strive to build solutions that boost your productivity.

Building APIs with ROR and GraphQL

  • By Arjunan Subramani
  • April 22, 2020
  • 894 Views

GraphQL is a query language and server-side runtime for API that prioritize and give the data exactly requested. GraphQL is designed to make APIs fast, flexible, and developer-friendly.
GraphQL is language-independent, so in ruby, we can implement using the graphql gem. This gives us a specific file structure and command-line tools to easily add GraphQL functionality to our Rails API.
In this tutorial, I will explain how to integrate GraphQL with Ruby On Rails Application.

Setting up a Rails App

we can create new rails application to firing the following command on terminal,

$ rails new graphql-rails --api

Change the directory to the newly created rails application and create the Author and Article model.

$ cd graphql-rails
$ rails g model author name:string bio:string
$ rails g model article title:string description:text author:references

Now we can add the association and validation to the models

# author.rb
class Author < ApplicationRecord
has_many :articles
validates :name, presence: true
end
# article.rb
class Article < ApplicationRecord
belongs_to :author
validates :title, :description, presence: true
end

Using $ rake db:create we can create the database and $ rake db:migrate will create the table inside the database. Once migration is completed we can add a few data to our database. Add the following object into db/seeds.rb file

# db/seeds.rb
author1 = Author.create(name: 'Arjunan', bio: 'Ruby developer')
author2 = Author.create(name: 'David', bio: 'Angular developer')
Article.create(
title: 'Basics of Ruby Programming',
description: 'This article is related to ruby programming',
author: author1
)
Article.create(
title: 'How to create Angular application',
description: 'This article is related to Angular App',
author: author2
)

Running the $ rake db:seed on the rails app root directory to add the data to the database.

Adding GraphQL

Add the graphql and graphiql-rails gem into Gemfile

# Gemfile
gem 'graphql'
group :development do
gem 'graphiql-rails'
end

Then run the bundle install to add the GraphQL to our rails application.

$ bundle install

Once the bundle install is completed then run the following command to generate GraphQL structure,

$ rails g graphql:install

It will create the graphql directory under app directory and creates the graphql specific controller app/controllers/graphql_controller.rb
Our routes.rb file looks like the following

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

We have added a gem to our development environment that lets you perform and visualize GraphQL queries on the fly. We need to extend our routes.rb file to only load this in the development environment of the app. That would look like the following:

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

GraphiQL is a GUI for GraphQL. GraphQL only has a single endpoint. Using this single endpoint we can fetch, create and modify the data.
We have created our rails API only app, so there is no concept of asset pipeline for testing the app in UI. Now we need to uncomment the require “sprockets/railtie” line in config/application.rb file and we also need to create manifest.js file like the following structure.
app
——assets
————config
—————–manifest.js
Add the following line into manifest.js file.

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

Now restart the rails server and visit the http://localhost:3000/graphiql it will give us a nice UI to use.

GraphQL Types

We need to add GraphQL objects/types to match our Models, so GraphQL knows what kind of data to send back in the event of a request. Here we will specify what columns, model methods, and more than return data to the application. As I told earlier, the GraphQL provides a command-line option for creating objects.

$ rails g graphql:object author
$ rails g graphql:object article

The above commands will create the author_type.rb and article_type.rb files under app/graphql/types directory. Now open the generated file and add the following code,

# article_type.rb
module Types
  class ArticleType < Types::BaseObject
  field :id, ID, null: false
  field :title, String, null: false
  field :description, String, null: true
  field :author, Types::AuthorType, null: true
  end
end
# author_type.rb
module Types
class AuthorType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
field :bio, String, null: true
field :articles, [Types::ArticleType], null: true
field :article_count, Integer, null: true
def article_count
object.articles.count
end
end
end

Each field gets an object type and a null option of whether or not it needs to be present for the query to succeed. This tells graphql what to expect from incoming and outgoing data.
In AuthorType we did not define functions for id, name, etc. Those fields are automatically mapped to the rails model attributes. We have defined article_count as a custom field, this does not exist in our model attributes, so we defined the method for this.
In GraphQL there are two main types are available

  1. Query Type
  2. Mutation Type

These have already been referenced when we ran the install generator and behave similarly like rails routes and resources. The file is named as rails_app_name_schema.rb

# app/graphql/graphql_rails_schema.rb
class GraphqlRailsSchema < GraphQL::Schema
mutation(Types::MutationType)
query(Types::QueryType)
end

Query

Using query we can fetch specific data from the API, queries are read-only like GET requests. Queries are more or less the same like REST but few differences:

  1. In REST, when we hit an endpoint /authors we will get all the user records with all the fields for each record(assuming the method that defines the endpoint does an Author.all). In a GraphQL query, we could just return the name or any field we want.
  2. In rest, to query for a record we did have to do a GET but in GraphQL, since we only need one endpoint, all our requests are POSTs and we specify whether it’s a mutation or query as part of the request.

All the queries in our app would reside in /types/query_type.rb as fields. We will define :articles and :article fields, along with articles and article functions. The articles field returns an array of ArticleType objects, and can never be nil (but can be empty). The article field accepts a required argument :id that is of the type ID, and returns a single ArticleType object.

# app/graphql/types/query_type.rb
module Types
class QueryType < Types::BaseObject
field :articles,[Types::ArticleType], null: false 
field :article, Types::ArticleType, null: false do
argument :id, ID, required: true
end
def articles
Article.all
end
def article(id:)
Article.find(id)
end
end
end

Visit https://localhost:3000/graphiql in your browser and paste in the following code for the articles and article query fields we added above. Here we specify exactly what we want the API to respond with; in this case, we only want a list of article id, title, and author name.

query {
articles {
id
title
author {
name
}
}
}

We can also query a single article using the following query,

query {
article(id: 1) {
id
title
author {
name
}
}
}

Mutation

In GraphQL using mutation we can modify the data in the database. It works like POST, PUT and DELETE operations in REST APIs.
Some terminology is in order when it comes to understanding mutations.

  • Arguments – arguments to accept as params, which are required, and what object types they are. This is similar to defining strong params in a Rails controller, but with more fine-grained control of what’s coming in.
  • Fields – Same concept as our Query fields from before. In my case, I accepted arguments to create a new article. I want to return an article field with our new model accompanied by an array of errors if any exist.
  • Resolver – The resolve method is where we execute our ActiveRecord commands. It returns a hash with keys that match the above field names.

Using the following command we can generate the mutation,

$ rails g graphql:mutation create_article

It will create the new file app/graphql/mutations/create_article.rb and update the app/graphql/types/mutation_type.rb file. Now mutation_type.rb is looks like following

# app/graphql/types/mutation_type.rb
module Types
class MutationType < Types::BaseObject
field :create_article, mutation: Mutations::CreateArticle
end
end

Now we add the following code in create_article.rb file,

# app/graphql/mutations/create_article.rb
module Mutations
class CreateArticle < BaseMutation
argument :title, String, required: true
argument :description, String, required: true
argument :author_id, Integer, required: true
field :article, Types::ArticleType, null: true
field :errors, [String], null: false 
def resolve(author_id:, **attributes)
return { errors: ["Author not exist"] } if !Author.find_by_id(author_id)
article = Author.find(author_id).articles.new(attributes)
if article.save
{ article: article, errors: [] }
else
{ article: nil, errors: article.errors.full_messages }
end
end
end
end

Creating an Article

Now we can build a query to create an article and return the same article or errors if present. Notice we pass an input: {} object to createArticle this maps to the :create_article field which accepts a single input argument.

mutation {
createArticle(input: {
title: "GraphQl Article", 
description: "GraphQl Article description", 
authorId: 1
}) {
article {
id
title
description
}
errors
}
}

Updating Article

Now we can generate the mutation file for update article,

$ rails g graphql:mutation update_article

It will create update_article.rb file under app/graphql/mutations directory and updates the mutation_type.rb file, Now mutation_type.rb is looks like following

# app/graphql/types/mutation_type.rb
module Types
class MutationType < Types::BaseObject
field :create_article, mutation: Mutations::CreateArticle
field :update_article, mutation: Mutations::UpdateArticle
end
end

Update the update_article.rb file like following,

# app/graphql/mutations/update_article.rb
module Mutations
class UpdateArticle < BaseMutation
argument :id, Integer, required: true
argument :title, String, required: true
argument :description, String, required: true
argument :author_id, Integer, required: true
field :article, Types::ArticleType, null: true
field :errors, [String], null: false
def resolve(id:, author_id:, **attributes)
article = Article.find_by_id(id)
return { errors: ["Article not exist"] } unless article
author = Author.find_by_id(author_id)
return { errors: ["Author not exist"] } unless author
article.assign_attributes(attributes)
if article.save
{ article: article, errors: [] }
else
{ article: nil, errors: article.errors.full_messages }
end
end
end
end

Now we can build a query to update an article and return the same article or errors if present.

mutation {
updateArticle(input: {
id: 18,
title: "Rails Application", 
description: "Rails Article description", 
authorId: 1
}) {
article {
id
title
description
}
errors
}
}

Delete Article

Similarly, we can delete the objects through mutations. Now we can create mutation for deleting the article

$ rails g graphql:mutation destroy_article

This will create destroy_article.rb mutation file and updates mutation_type.rb file like following

module Types
  class MutationType < Types::BaseObject
    field :create_article, mutation: Mutations::CreateArticle
    field :update_article, mutation: Mutations::UpdateArticle
    field :destroy_article, mutation: Mutations::DestroyArticle
  end
end

Update the destroy_article.rb file like following,

module Mutations
class DestroyArticle < BaseMutation
argument :id, Integer, required: true
field :message, String, null: true
field :error, String, null: true
def resolve(id:)
article = Article.find_by_id(id)
return { error: "Article not exist" } unless article
article.destroy!
return { message: "Article was deleted sucessfully" }
end
end
end

Now we can build a query to delete an article.

mutation {
destroyArticle(input: {
id: 18
}) {
message
error
}
}


This is how we build APIs with GraphQL and Ruby on Rails. Using GraphQL we don’t need extra routes, controllers, or serializers. For more details visit graphql.org and graphql-ruby.org.
Do you find it interesting? you might also like these articles. Top 10 Best Tech Companies For Employees To Work In The USA In 2020 and Top 10 IT Staffing and Recruiting Agencies in the USA.
If you have a business idea in your mind and in search of a reliable web development company, you are in the right place. Hire the best Ruby on Rails developers in the industry from Agira technologies.

Looking for a Tech partner to dominate the digital world?

 

Arjunan Subramani

Senior Software Engineer | Around 4 Years of experience in Web development and Testing arena. Plus, hands on expertise in Ruby, Ruby on Rails, Watir, Ruby Cucumber.