GraphQL with Ruby On Rails: Queries in Multiple Files

GraphQL with Ruby On Rails: Queries in Multiple Files

<strong>In the last episode, we added RSpecs tests for GraphQL mutations. If you need some more insight about the topic you can check the article here. Today we are going to add specs (again, we will focus only on the happy paths) for GraphQL queries. But there is one thing about the current implementation of the queries that I don’t like. We have everything defined in one file: app/graphql/types/query_type.rb</strong>

In the last episode, we added RSpecs tests for GraphQL mutations. If you need some more insight about the topic you can check the article here. Today we are going to add specs (again, we will focus only on the happy paths) for GraphQL queries. But there is one thing about the current implementation of the queries that I don’t like. We have everything defined in one file: app/graphql/types/query_type.rb

module Types
  class QueryType < Types::BaseObject
    field :authors, [Types::AuthorType], null: false do
      description 'Find all authors'
    end
field :author, Types::AuthorType, null: false do
  description 'Find an author by ID'
  argument :id, ID, required: true
end

field :books, [Types::BookType], null: false do
  description 'Find all books'
end

field :book, Types::BookType, null: false do
  description 'Find a book by ID'
  argument :id, ID, required: true
end

def authors
  Author.all
end

def author(id:)
  Author.find(id)
end

def books
  Book.all
end

def book(id:)
  Book.find(id)
end

end
end

You can imagine that this file will grow over time. I want to show you how to divide the logic into many smaller pieces.

The first step of every refactoring

It is hard to do refactoring without proper specs. So we are going to add specs first. But to make the article a little shorter and easier to comprehend, we will embrace the approach we put a great emphasis on in our company. We will do a test-driven development. That means that we will write the final specs. So instead of adding specs to our previous implementation, we will add specs for the future implementation where every query is defined in a separate file.

We would like to have a structure where all queries are inside app/graphql/queries/ folder. Therefore our specs will end up in specs/graphql/queries/ directory.

specs/graphql/queries/author_spec.rb

require 'rails_helper'

module Queries
RSpec.describe Author, type: :request do
describe '.resolve' do
it 'returns author for provided id' do
author = create(:author, first_name: 'Lee', last_name: 'Child', date_of_birth: Date.parse('1954-10-29'))
book = create(:book, author: author)

    post '/graphql', params: { query: query(id: author.id) }

    json = JSON.parse(response.body)
    data = json['data']['author']

    expect(data).to include(
      'id'          =&gt; be_present,
      'firstName'   =&gt; 'Lee',
      'lastName'    =&gt; 'Child',
      'dateOfBirth' =&gt; '1954-10-29',
      'books'       =&gt; [{ 'id' =&gt; book.id.to_s }]
    )
  end
end

def query(id:)
  &lt;&lt;~GQL
    query {
      author(id: #{id}) {
        id
        firstName
        lastName
        dateOfBirth
        books {
          id
        }
      }
    }
  GQL
end

end
end

specs/graphql/queries/book_spec.rb

require 'rails_helper'

module Queries
RSpec.describe Authors, type: :request do
describe '.resolve' do
it 'returns all authors' do
author = create(:author, first_name: 'Lee', last_name: 'Child', date_of_birth: Date.parse('1954-10-29'))
create(:author, first_name: 'Stephen', last_name: 'King', date_of_birth: Date.parse('1947-09-21'))
book = create(:book, author: author)

    post '/graphql', params: { query: query }

    json = JSON.parse(response.body)
    data = json['data']['authors']

    expect(data).to match_array [
      hash_including(
        'id'          =&gt; be_present,
        'firstName'   =&gt; 'Lee',
        'lastName'    =&gt; 'Child',
        'dateOfBirth' =&gt; '1954-10-29',
        'books'       =&gt; [{ 'id' =&gt; book.id.to_s }]
      ),
      hash_including(
        'id'          =&gt; be_present,
        'firstName'   =&gt; 'Stephen',
        'lastName'    =&gt; 'King',
        'dateOfBirth' =&gt; '1947-09-21',
        'books'       =&gt; []
      )
    ]
  end
end

def query
  &lt;&lt;~GQL
    query {
      authors {
        id
        firstName
        lastName
        dateOfBirth
        books {
          id
        }
      }
    }
  GQL
end

end
end

specs/graphql/queries/book_spec.rb and

specs/graphql/queries/books_spec.rb are almost the same so I’am not going to paste the code in here. Instead, you can check the files on gist if you like: book_spec.rb and books_spec.rb

As you can see our new specs look really similar to those we wrote for mutations. We have a query method that we use for building GraphQL query and we check if the result of the query meets our expectations.

Dividing queries into separate files

Now when all needed specs are in place we can finally do the refactoring. I am not going to show a proper TDD in this article because it will require those actions over and over again: run spec, see the error, fix the error, run spec again to see another error, fix that error, etc. It will be hard to follow so I’m going to present the final solution and explain the idea behind it.

We want to have a similar structure to what we have in mutations but it is not supported and we need to look for alternatives. The one we usually use for splitting queries is Resolvers.

Disclaimer: it is not always recommended. Please check the documentation for more information.

As I mentioned many times, we want our query_type.rb to work similar to mutation_type.rb it means that a new version of app/graphql/types/query_type.rb should look like this:

module Types
class QueryType < Types::BaseObject
field :author, resolver: Queries::Author
field :authors, resolver: Queries::Authors
field :book, resolver: Queries::Book
field :books, resolver: Queries::Books
end
end

We removed all the logic we had. Instead, we define what query is available and the resolver for that query (a place when we can find it).

If we are going to follow the mutations’ way of doing things we need an additional file: app/graphql/queries/base_query.rb

module Queries
class BaseQuery < GraphQL::Schema::Resolver
end
end

It is empty for now, but in the future episodes, we will use it for some cool things.

We can finally see the implementation of our new queries:

app/graphql/queries/author.rb

module Queries
class Author < Queries::BaseQuery
description 'Find an author by ID'

argument :id, ID, required: true

type Types::AuthorType, null: false

def resolve(id:)
  ::Author.find(id)
end

end
end

app/graphql/queries/authors.rb

module Queries
class Authors < Queries::BaseQuery
description 'Find all authors'

type [Types::AuthorType], null: false

def resolve
  ::Author.all
end

end
end

app/graphql/queries/book.rb

module Queries
class Book < Queries::BaseQuery
description 'Find a book by ID'

argument :id, ID, required: true

type Types::BookType, null: false

def resolve(id:)
  ::Book.find(id)
end

end
end

app/graphql/queries/books.rb

module Queries
class Books < Queries::BaseQuery
description 'Find all books'

type [Types::BookType], null: false

def resolve
  ::Book.all
end

end
end

The code of each query is moved from the query_type.rb without any changes. The magic that makes it possible is the inheritance from Queries::BaseQuery which inherits from GraphQL::Schema::Resolver. That is it.

Summary

At this point we have all the endpoints tested out and we (almost) have a structure that is easy to build on. In the future episodes, we will discuss things like pagination, authentication, authorization and many more.

Originally published by Ireneusz Skrobiś at https://itnext.io

Learn More

☞ The Complete Ruby on Rails Developer Course

☞ Learn to Code with Ruby

☞ Comprehensive Ruby Programming

☞ Dissecting Ruby on Rails 5 - Become a Professional Developer

Ruby vs Ruby on Rails web framework

Rails is a development tool which gives web developers a framework, providing structure for all the code they write. The Rails framework helps developers to build websites and applications. Ruby is a programming language stronger than Perl and more object-oriented than Python. It is being developed with increasing productivity.

Ruby on Rails vs PHP

Ruby on Rails vs PHP

Understanding the pros and cons of Ruby on Rails versus PHP is important when deciding how to create your business-critical applications.

Originally published at https://www.engineyard.com

There’s more than one way to build a web application. No matter what type of application you are trying to create, your programmers have their preferred approach and their preferred code languages to accomplish the task. In the world of web applications, most program developers have to decide between Ruby on Rails versus PHP.

Ruby on Rails consists of Ruby, which is a scripting language, and Rails, which is a web development framework; PHP is a server-side scripting language. Both programming languages have been around since the mid-1990s, but PHP rules the web, while Ruby on Rails is more popular for business web applications. Understanding the pros and cons of Ruby on Rails versus PHP is important when deciding how to create your business-critical applications.

Ruby on Rails Versus PHP at First Glance

Both Ruby on Rails and PHP are open source, so there are no licensing fees. However, because PHP is used to run most of today’s web systems, there are more PHP programmers than Ruby developers, which means there is a larger pool of PHP experts and a larger open source library to draw from.

Part of the reason PHP is more popular with web developers is because it is easier to learn. PHP is also an object-oriented programming language, which makes it easier to be more creative and tackle tougher software challenges.

Once web developers master PHP, many of them choose to add Ruby on Rails to their expertise because of the advantages and power that Ruby on Rails offers for business application development. Ruby and Rails were created together to deliver web solutions, and the primary difference between PHP and Ruby on Rails is that Rails requires you to understand the full stack, including the web server, application server, and database engine.

Since both Ruby and PHP are open source, the support of the programming communities is an important differentiator. PHP has more deployments so it has a larger developer community, but the Ruby on Rails community is very skilled and enthusiastic and they want to share, so there is a growing library of ready-to-use Ruby gems.

Differences in Deployment

When it comes to deployment, PHP is very easy to implement. You simply transfer files to the web server via FTP and that’s it. With PHP, you don’t need to worry about the web stack. Most hosting services use a combination of open source for the stack, including Linux, Apache, MySQL, and PHP (LAMP), so once the files are loaded, they just run. That’s the advantage of server-side software.

Ruby on Rails is more complex to deploy because you have to know the full stack. That means knowing the details of the web server (e.g., Apache or NginX), as well as the database. You have to go through more steps, such as precompiling assets to make sure all the right files are there. This is the price of being able to design and deploy more complex applications.

Where Ruby on Rails really shines is in the software development process itself. Since Ruby is an object-intensive language, everything is an object, including classes and modules, with Rails providing an integrated test framework. PHP is not always object-oriented, so coding can be laborious and time-consuming. Applications can be built and tested in Ruby on Rails much faster than in PHP, so even if there is some debugging involved, Ruby on Rails dramatically reduces the time to deployment.

As noted above, PHP applications are relatively simple to deploy since there is no stack to worry about, and they are relatively inexpensive to host. Hosting Ruby on Rails applications is another story. Not all hosting providers will support Ruby on Rails, and those that do usually add additional a la carte fees because Ruby applications require more services.

The Business Case for Ruby on Rails versus PHP

While it’s clear that Ruby is a more difficult programming language to master, in many ways, it is a more robust language that is better suited for creating business applications. PHP was created specifically for the web, but Ruby on Rails offers much more.

For one thing, Ruby on Rails applications tend to be cleaner and more compact. Because PHP is so simple, it lends itself to sloppy coding that can be impossible to maintain. Ruby has the advantage of being more elegant and concise, and the documentation for Ruby applications tends to be generated with the code so anyone can make revisions or upgrades.

Most importantly, Ruby on Rails lends itself to agile software practices and rapid application development (RAD). Rails is a mature framework that allows programmers to create maintainable software, and it has integrated testing tools that shorten the developer cycle. When you consider the cost of talented programmers (and you know that time is money), reducing development time can mean substantial savings.

Depending on your business development needs, you may be leaning toward PHP or Ruby on Rails. Each has its strengths and weaknesses, but Ruby on Rails continues to gain popularity for business-critical and e-commerce applications because of its versatility, scalability, and upgradability. In the end, you have to consider which language will deliver a cleaner, more stable application that can evolve and grow with your business.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Learn More

PHP for Beginners - Become a PHP Master - CMS Project

Learn Object Oriented PHP By Building a Complete Website

PHP OOP: Object Oriented Programming for beginners + Project

Laravel PHP Framework Tutorial - Full Course for Beginners (2019)

Symfony PHP Framework - Full Course

What is a Full Stack Developer?

Build RESTful API In Laravel 5.8 Example

Laravel 5.8 Tutorial from Scratch for Beginners

Build a CRUD Operation using PHP & MongoBD

Build a CRUD Web App with Ruby on Rails

Ruby on Rails Tutorial for Beginners

How To Use PostgreSQL with Your Ruby on Rails Application on macOS

Python, Ruby, and Golang: A Command-Line Application Comparison