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
- Query Type
- 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:
- 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.
- 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.