Writing GraphQL queries in native Ruby = Love ❤️

20181119 graphql ruby 01

GraphQL has made waves with front end developers, but if you’re a backend developer, chances are it hasn’t infiltrated your world –– you might not even know what it is. And it’s not your fault. There has been a limited amount of blog posts, talks and information around using GraphQL APIs on languages that don’t have an official Apollo client.

I'll be speaking at DeveloperWeek 2019 so come on over to watch an in-depth talk about this library and advanced use-cases!

What is GraphQL and why does it matter for back end developers? GraphQL is a query language and ecosystem of accompanying tools that was launched by Facebook in 2015. It’s been praised by front end developers for providing a great developer experience.

Let’s take a look into the current GraphQL landscape in Ruby, what’s available, what’s missing and how we can introduce GraphQL into our Ruby environments to take advantage of its power.

The benefits of GraphQL

In a previous post by Paolo Negri, our CTO, he describes why Contentful believes that GraphQL is one of the most interesting technologies that is available for developers.

To summarize his post, we can boil it down to three key points:

  • GraphQL is an expressive query language that allows for granular and network-efficient control over retrieved data with a single request.
  • It has type-safe API definitions with the ability to easily evolve and extend them.
  • It includes wide tooling support, backed by some of the biggest companies, such as Facebook, Github, Shopify, and more.

So, what's holding us back?

For backend languages, such as Python, .Net or Ruby, there hasn't been much adoption of GraphQL API consumption. While there are great tools for serving GraphQL APIs like Graphene for Python, or GraphQL-Ruby for Ruby, there are no first-class libraries for consuming GraphQL.

Presently-available consumer libraries do not provide any native mechanisms to support GraphQL — with popular clients such as Github's GraphQL consumer requiring the use of strings. While it provides schema introspection and validation, unlike its counterpart for Python, both clients are tied to using strings to define queries and have had few recent updates.

Why the defining of queries as strings isn’t great

While working with GraphQL queries as strings in your codebase is a good starting point for learning the semantics of the GraphQL language, treating queries as strings makes creating dynamic queries more cumbersome. At Contentful, we believe that native implementations of the GraphQL syntax enable the query language to be more flexible and customizable. A native implementation greatly simplifies the dynamic creation of queries, enabling the conditional addition or removal of query elements based on state and other criteria.

For example, dynamically adding a field to a query with string interpolation bloats the query code like in the following instance:

This is the string-based implementation

1
2
3
4
5
6
7
8
9
10
Query = GQL_RUBY_CLIENT.parse <<~GRAPHQL
  query {
    catCollection {
      items {
        #{object.shows_name? ? 'name' : ''}
        lives
      }
    }
  }
GRAPHQL

In contrast, constructing the same query based on the same conditions would be more natural with the following syntax:

1
2
3
4
5
6
7
8
9
# This is the equivalent native implementation
Query = query {
  catCollection {
    items {
      name if object.shows_name?
      lives
    }
  }
}

But… the future is bright!

We at Contentful would like to see an increase in the adoption of GraphQL adoption, so we are investing in creating an ecosystem for developers who want to start using (or increase their usage of) GraphQL in their applications.

So, we're introducing GQLi, a GraphQL consumer domain-specific language that allows you to write GraphQL queries in native Ruby.

What does this mean?

Let us show you the most basic example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'gqli'

SPACE_ID = 'cfexampleapi'
ACCESS_TOKEN = 'b4c0n73n7fu1'
CONTENTFUL_GQL = GQLi::Client.new(
  "https://graphql.contentful.com/content/v1/spaces/#{SPACE_ID}",
  headers: { "Authorization" => "Bearer #{ACCESS_TOKEN}" }
)

CatQuery =  GQLi::DSL.query {
  catCollection(limit: 10) {
    items {
      name
      likes
      lives
    }
  }
}

result = CONTENTFUL_GQL.execute(CatQuery)

puts result.data

As you can see, there are no strings involved in this code snippet. You can even copy & paste the query into our GraphiQL explorer, and it will work.

But what about more complex queries?

GQLi implements everything that you would be expecting from a modern GraphQL client implementation, including fragments and type matchers, along with schema introspection and validation.

Let's take an example of what a more complex query composed of multiple fragments would look like. For this example, we'll be using the introspection query that the client itself uses:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
module GQLi
  # Introspection schema and validator
  class Introspection
    extend DSL

    # … other fragment definitions ...

    # Type introspection fragment
    FullType = fragment('FullType', '__Type') {
      kind
      name
      description
      fields(includeDeprecated: true) {
        name
        description
        args { ___ InputValue }
        type { ___ TypeRef }
        isDeprecated
        deprecationReason
      }
      # ... and more ...
    }

    # Query for fetching the complete schema
    IntrospectionQuery = query {
      __schema {
        queryType { name }
        mutationType { name }
        subscriptionType { name }
        types { ___ FullType }
        directives {
          name
          description
          args { ___ InputValue }
          onOperation
          onFragment
          onField
        }
      }
    }

    # ... the rest of the introspection code
  end
end

As you can see, in more complex cases, we're still using mostly the same syntax as GraphQL, with a tiny few changes:

  • Fragments are declared as fragment('FragmentName', 'OnType') { ... } instead of fragment FragmentName on Type { ... }
  • Fragment inclusion is done with ___ Fragment instead of ... Fragment

And there is one more difference not shown in this example that is type matching; which, in this library, is done with __on('TypeName') { ... } instead of ... on TypeName { ... }.

Make GraphQL a first class citizen for backend developers

GQLi provides a better way to interface with GraphQL APIs in your Ruby applications by providing the flexibility of working with the entirety of the Ruby language.

We hope that by providing first-class tooling for GraphQL on Ruby, there will be a huge increase in adoption of this amazing technology and all the benefits that it brings..

What’s next?

We're actively working on GQLi, and other tools and guides, as our push for GraphQL continues.

For this tool, we're looking forward to providing much better validation error messages, and include mutation and subscription capabilities soon.

We'd love to receive your feedback and if you want to contribute, please feel free to open up an issue or a pull request on our Github repository.

Interested in this library and advanced use-cases? I'll be speaking at DeveloperWeek 2019 so come on over to watch my in-depth talk and more!

Blog posts in your inbox

Subscribe to receive most important updates. We send emails once a month.