DEV Community

Andrew Luchuk
Andrew Luchuk

Posted on

GraphQL Basics

GraphQL is a relatively new query language originally developed by Facebook in 2012. The primary goal of GraphQL is to reduce the amount of unnecessary data that an API query returns.

GraphQL allows developers to retrieve associated objects in a single query. REST APIs, on the other hand would require multiple queries for the same data.

Writing GraphQL queries requires a little more of a learning curve than REST queries, but once learned, GraphQL is fairly easy to understand.

I’ve decided to split what was originally planned to be one article into two, because I think it will be easier to process everything in smaller chunks.

GraphQL Basics

Before jumping right in to writing code with GraphQL, it is critical to understand a little bit about how it works in the background.

GraphQL, like REST can be implemented in any programming language, and there are already libraries in a bunch of languages to help get new users started. In part 2 of this series, we installed the ruby implementation of GraphQL

One of the key features of GraphQL is its type system. For those of us who don’t have CS degrees, a type is essentially a description of what kinds of data are represented by the type's name.

For example: a common type is the integer type. The integer type only allows instances of it to contain positive or negative whole numbers. Another common type is the string type. Strings allow any sequence of characters to be contained within instances of strings. For more details of what data types are and how they are used, checkout the Wikipedia article on data types.

GraphQL has two fundamental kinds of types: object types which we will use to build queries, and scalar types which represent concrete data. The uses of these two different types should become clearer as we progress.

Scalar Types

GraphQL has five built-in scalar types. We will use these five types when we start building our object types later. The five built-in types are: String, Int, Float, Boolean, and ID.

Strings represent any kind of string data, Ints represent positive and negative whole numbers, Floats represent any positive or negative real number, including those with decimal points, Booleans represent true or false values, and IDs represent the id of an object in the underlying database system.

Object Types

Object types are mostly developer defined and are composed of any combination of the five scalar types along with other object types.

Object types are what we will focus on, because the object types we build near the end will allow us to query the backend using GraphQL. Essentially, we will simply translate the models defined in part 2 into GraphQL types.

GraphQL Queries

Queries are the glue of GraphQL that bind the frontend to the backend. Take a look at a basic GraphQL query:

query {
    users {
        id
        name
        username
    }
}
Enter fullscreen mode Exit fullscreen mode

Here is a breakdown of the parts of the query:
GraphQL Query breakdown

Suppose we wanted to get the titles of all the topics this user has created too, all we have to do is add the following:

query {
    users {
        id
        name
        username
        topics {
            title
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The result of this query would include all of the titles of the topics each user has created. When writing GraphQL queries, remember that all queried objects must resolve to scalar types at some point.

Building our GraphQL schema

The GraphQL gem provides a bunch of useful generators to help us build object types quickly.

Run the following command in the project's root directory:

rails g graphql:object User username:String name:String email:String id:ID comments:[Reply] likes:[Like] topics:[Topic]
Enter fullscreen mode Exit fullscreen mode

The User argument is the name of the new GraphQL type. Each of the other arguments follows the format <field_name>:<field_type>. So, username:String means add a field named username with a type of String. These fields map to the attributes with the same names on our models. Fields like comments:[Reply] tell graphql three things:

  1. Make a field named comments.
  2. Make the new field a list. The brackets around the type tell GraphQL to use an array.
  3. For the List's type, use the Reply type.

We have not made the Reply type yet so go ahead and run the following commands to finish building the base types:

rails g graphql:object Topic title:String content:String id:ID user:User likes:[Like] replies:[Reply]
rails g graphql:object Like user:User post:Post id:ID
rails g graphql:object Reply content:String id:ID user:User post:Post
Enter fullscreen mode Exit fullscreen mode

In order to support the polymorphic associations we set up in part 2, we will need to create the union type Post. A union type is a type that can represent one of several different types. Run the following command:

rails g graphql:union Post Reply Topic
Enter fullscreen mode Exit fullscreen mode

There seems to be a bug with the union type generator, so it might be necessary to alter post_type a little:

# app/graphql/types/post_type.rb
module Types
  class PostType < Types::BaseUnion
    # Instead of the following line of code you may see:
    # possible_types [Types::ReplyType, Types::TopicType]
    # If you do, this is a bug. Remove the brackets to fix it.
    possible_types Types::ReplyType, Types::TopicType
  end
end
Enter fullscreen mode Exit fullscreen mode

Now that we have generated all the base types, we need to make them queryable. Open up query_type.rb, remove the default test_field and add the following code:

# app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    # Add root-level fields here.
    # They will be entry points for queries on your schema.

    field :users, [Types::UserType], null: false, description: 'Returns all the users'

    def users
      User.all
    end

    field :topics, [Types::TopicType], null: false, description: 'Returns all the topics'

    def topics
      Topic.all
    end

    field :topic, Types::TopicType, null: true do
      argument :id, ID, required: true
    end

    def topic(id:)
      Topic.find_by(id: id)
    end

    field :user, Types::UserType, null: true do
      argument :id, ID, required: true
    end

    def user(id:)
      User.find_by(id: id)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

The field shortcuts here are very similar to those that the commands generated. The first argument (e.g. :users) is the name of the field, the second argument is the type of the field, the third determines whether the return value can be null, and the last is a description of the field. The argument lines declare that the field requires an argument, which we will cover in more detail later in the series.

Running Test Queries

If you want to test our new GraphQL back end, you can do so using either GraphiQL or Postman.

GraphQL is a very powerful library that can greatly reduce API development time. GraphQL takes a little more work to understand than REST APIs, but I find the reward well worth the effort. Thank you for reading!

As usual, the code is available in the GitHub repo

Top comments (0)