• Stars
    star
    212
  • Rank 186,094 (Top 4 %)
  • Language
    Ruby
  • License
    MIT License
  • Created about 6 years ago
  • Updated about 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Ruby GraphQL Client for Humans

GQLi - GraphQL Client for humans

GQLi is a DSL (Domain Specific Language) to consume GraphQL APIs.

DISCLAIMER:

This gem is in stable state and will be updated with more features. You can start using it for your respective projects, although there is no support available from Contentful.

This gem is not covered under the Contentful SLAs.

Users are free to create Github issues and collaborate.

Installation

Install it via the command line:

gem install gqli

Or add it to your Gemfile:

gem 'gqli'

Usage

Creating a GraphQL Client

For the examples throughout this README, we'll be using the Contentful and Github GraphQL APIs, for which we have factory methods. Therefore, here's the initialization code required for both of them:

require 'gqli'

# Creating a Contentful GraphQL Client
SPACE_ID = 'cfexampleapi'
CF_ACCESS_TOKEN = 'b4c0n73n7fu1'
CONTENTFUL_GQL = GQLi::Contentful.create(SPACE_ID, CF_ACCESS_TOKEN)

# Creating a Github GraphQL Client
GITHUB_ACCESS_TOKEN = ENV['GITHUB_TOKEN']
GITHUB_GQL = GQLi::Github.create(GITHUB_ACCESS_TOKEN)

Note: Please feel free to contribute factories for your favorite GraphQL services.

For creating a custom GraphQL client:

require 'gqli'

# Create a custom client
GQL_CLIENT = GQLi::Client.new(
  "https://graphql.yourservice.com",
  headers: {
    "Authorization" => "Bearer AUTH_TOKEN"
  }
)

Creating a Query

Queries are the way to request data from a GraphQL API. This gem provides a simple DSL to create your own queries.

The query operator is GQLi::DSL.query.

# Query to fetch the usernames for the first 10 watchers of the first 10 repositories I belong to
WatchersQuery = GQLi::DSL.query {
  viewer {
    login
    repositories(first: 10) {
      edges {
        node {
          nameWithOwner
          watchers(first: 10) {
            edges {
              node {
                login
              }
            }
          }
        }
      }
    }
  }
}

Divide and conquer - using Fragments

In order to reuse parts of queries, we can split chunks of our queries into Fragments.

The fragment operator is GQLi::DSL.fragment.

To include fragments within other nodes, use the ___ operator as shown below.

To do type matching, use the __on operator as shown below.

# Base fragment that will be reused for all Cat queries.
CatBase = GQLi::DSL.fragment('CatBase', 'Cat') {
  name
  likes
  lives
}

CatBestFriend = GQLi::DSL.fragment('CatBestFriend', 'Cat') {
  bestFriend {
    # Here, because `bestFriend` is polimorphic in our GraphQL API,
    # we need to explicitly state for which Type we want to include our fragment.
    # To do a type match, instead of GraphQLs `... on SomeType` we do `__on('SomeType')`.
    __on('Cat') {
      # To include a fragment, instead of GraphQLs `...`, we use `___`.
      ___ CatBase
    }
  }
}

# A fragment reusing multiple fragments
CatImportantFields = GQLi::DSL.fragment('CatImportantFields', 'Cat') {
  ___ CatBase
  ___ CatBestFriend
}

# A fragment used to define a query, alongside other regular fields.
CatQuery = GQLi::DSL.query {
  catCollection(limit: 1) {
    items {
      ___ CatImportantFields
      image {
        url
      }
    }
  }
}

Executing the queries

To execute the queries, you need to pass a Query object to the client's #execute method. This will return a Response object which contains the data and the query executed.

For example:

response = CONTENTFUL_GQL.execute(CatQuery)

puts "Query sent:"
puts response.query.to_gql

puts
puts "Response received"
response.data.catCollection.items.each do |c|
  puts "Name:    #{c.name}"
  puts "Likes:   #{c.likes.join(", ")}"
  puts "Lives #: #{c.lives}"
  c.bestFriend.tap do |bf|
    puts "Best Friend:"
    puts "\tName:    #{bf.name}"
    puts "\tLikes:   #{bf.likes.join(", ")}"
    puts "\tLives #: #{bf.lives}"
  end
end

The output is:

Query sent:
query {
  catCollection(limit: 1) {
    items {
      name
      likes
      lives
      bestFriend {
        ... on Cat {
          name
          likes
          lives
        }
      }
      image {
        url
      }
    }
  }
}

Response received
Name:    Happy Cat
Likes:   cheezburger
Lives #: 1
Best Friend:
	Name:    Nyan Cat
	Likes:   rainbows, fish
	Lives #: 1337

Schema Introspection and Validation

By default this library will fetch and cache a copy of the GraphQL Schema for any API you create a client for.

This schema is used for query validation before running queries against the APIs. In case a query is invalid for the given schema, an exception will be raised.

To disable schema caching completely, when you initialize your client, send validate_query: false.

Queries executed using the #execute method on the client will be validated before executing the request if the option is set to true (which it is by default).

To avoid validating a query, you can use #execute! instead.

To validate the query outside of the scope of an HTTP request, you can use MY_CLIENT.schema.valid?(query).

Embedding the DSL in your classes

If you want to avoid the need for prepending all GQLi DSL's calls with GQLi::DSL., then you can extend and/or include the module within your own classes. When using extend, you will have access to the DSL at a class level. When using include you will have access to the DSL at an object level.

class ContentfulClient
  extend GQLi::DSL # Makes DSL available at a class level
  include GQLi::DSL # Makes DSL available at an object level

  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}" }
  )

  CatBase = fragment('CatBase', 'Cat') {
    name
    likes
    lives
  }

  CatBestFriend = fragment('CatBestFriend', 'Cat') {
    bestFriend {
      __on('Cat') {
        ___ CatBase
      }
    }
  }

  CatImportantFields = fragment('CatImportantFields', 'Cat') {
    ___ CatBase
    ___ CatBestFriend
  }

  def cats(limit)
    CONTENTFUL_GQL.execute(
      query {
        catCollection(limit: limit) {
          items {
            ___ CatImportantFields
            image {
              url
            }
          }
        }
      }
    )
  end
end

response = ContentfulClient.new.cats(5)

Dealing with name collisions

By defining queries via a DSL, you may sometimes find that the fields you query for are also the names of built-in methods or reserved keywords of the language.

To avoid collisions you can use the __node helper, for example:

query = GQLi::DSL.query {
  catCollection {
    items {
      sys {
        __node('id')
      }
    }
  }
}

The helper method __node, can also receive arguments and have children nodes as expected from any other node declaration, for example:

query = GQLi::DSL.query {
  __node('catCollection', limit: 5) {
    items {
      name
    }
  }
}

Directives

In GraphQL, nodes can be selectively included or removed by the usage of directives, there are 2 directives available for the querying specification to do this: @include and @skip.

query = GQLi::DSL.query {
  someNode(:@include => {if: object.includes_some_node?})
}

This will get transformed to:

query {
  someNode @include(if: true) # or false
}

This behaviour is equivalent to using native Ruby to include/exclude a field from the query:

query = GQLi::DSL.query {
  someNode if object.includes_some_node?
}

The difference is that by using the native implementation, in case of the condition not being met, the node will be completely not included in the outgoing query.

Enums

Enums are a list of predefined values that are defined on the type system level. Since Ruby doesn't have built-in Enums that can be translated directly into GraphQL Enums, we created the __enum helper that you can use within your queries to transform your values into an Enum value.

query = GQLi::DSL.query {
  catCollection(order: __enum('lives_ASC')) {
    items {
      name
    }
  }
}

This will render to:

query {
  catCollection(order: lives_ASC) {
    items {
      name
    }
  }
}

Aliases

There may be times where it is useful to have parts of the query aliased, for example, when querying for pinned and unpinned articles for a news site.

This can be accomplished as follows:

ArticleFragment = GQLi::DSL.fragment('ArticleFragment', 'ArticleCollection') {
  items {
    title
    description
    heroImage {
      url
    }
  }
}

query = GQLi::DSL.query {
  __node('pinned: articleCollection', where: {
    sys: { id_in: ['articleID'] }
  }) {
    ___ ArticleFragment
  }
  __node('unpinned: articleCollection', where: {
    sys: { id_not_in: ['articleID'] }
  }) {
    ___ ArticleFragment
  }
}

Get involved

PRs Welcome

We appreciate any help on our repositories.

License

This repository is published under the MIT license.

Code of Conduct

We want to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers.

Read our full Code of Conduct.

More Repositories

1

Concorde

Download and decode progressive JPEGs on iOS.
Objective-C
1,441
star
2

kube-secret-syncer

A Kubernetes operator to sync secrets from AWS Secrets Manager
Go
197
star
3

contentful-aws-lambda-static

An experiment in static site genration using Contentful and AWS Lambda
JavaScript
129
star
4

coredns-nodecache

Nodelocal DNS implementation as a CoreDNS plugin
Go
75
star
5

terraform-diff

Always know where you need to run Terraform plan & apply!
Go
64
star
6

contentful-go

Contentful api SDK for GoLang
Go
44
star
7

contentful-metalsmith-example

Simple project to show the usage of the contentful-metalsmith plugin
HTML
32
star
8

terraform-contentful

Terraform provider for Contentful
Go
29
star
9

ui-editable-table

A UI-Extension to enrich the editor with an editable table
HTML
28
star
10

product-catalogue-web.ts

Demo application in Typescript / Angular2 to consume spaces from Contentful which implement the product catalogue template.
TypeScript
27
star
11

file-upload-example

Example application to demonstrate the new direct file upload feature of Contentful.
JavaScript
21
star
12

keepachangelog

Parse, modify, and create change logs
JavaScript
20
star
13

contentful-importer.rb

Generic Contentful importer.
Ruby
19
star
14

contentful.py

This project is unofficial and currently unsupported, the official SDK can be found here: https://github.com/contentful/contentful.py
Python
18
star
15

continous-delivery-environments-example

An example application for how you can Integrating migrations in your continous delivery pipeline.
JavaScript
18
star
16

gazette

Contentful Gazette: bidirectional real-time content management and delivery
JavaScript
16
star
17

gitify-dependencies

CLI tool for replacing node dependencies with their respective git repositories.
JavaScript
15
star
18

md-to-html-lambda

JavaScript
8
star
19

webhook-apigateway-lambda-slack.js

Demo setup of an integration of Contentful's webhooks with a slack channel.
JavaScript
8
star
20

zappa-contentful

Example application showing how to build an entirely serverless site with Python, Flask, and Contentful. Then seamlessly deploy it to AWS Lambda/API Gateway using Zappa.
CSS
8
star
21

gatsby-starter-contentful-knowledge-base

A gatsby starter for knowledge base apps powered by Contentful.
JavaScript
7
star
22

lookbook-example

Browser app example for createing a Lookbook with Contenful https://www.contentful.com/blog/2015/09/10/creating-a-digital-lookbook/
JavaScript
7
star
23

continous-delivery-environments-tutorial-starter

Starting Repo for continous delivery pipeline tutorial. For a completed project see https://github.com/contentful-labs/continous-delivery-environments-example
Python
7
star
24

contentful-action-example

Example repo showcasing how to use Contentful Action Repo
JavaScript
6
star
25

cosmonaut

Explore the awesome depths of Spaces (to be read in Neil deGrasse Tyson's voice)
JavaScript
6
star
26

cdn-webinar-store-demo

The "Store" demo from the Content Delivery API Webinar on 2014.07.10
CSS
6
star
27

contentful-cardboard

Google Cardboard VR displaying entries from Contentful
Java
5
star
28

droidstory

Sample Android app using Vault.
Java
5
star
29

ui-country-select

A UI-Extension to enrich the editor with a country select
HTML
5
star
30

super-chemex-bot

Our friendly bot-overlord notifying people when there is freshly brewed coffee in the kitchen.
JavaScript
4
star
31

ui-reference-quick-select

A UI-Extension to enrich the editor with a reference quick select
HTML
4
star
32

contentful-algolia-integration

JavaScript
3
star
33

cma_import_script

Sample script using the contentful management api to create a space with data.
Ruby
3
star
34

spec-super-chemex-bot

Super Chemex Bot - Slack Spec Edition
JavaScript
3
star
35

contentful_mirror

Contentful Module for Magic Mirror
JavaScript
2
star
36

reposite-contentful-template

Reposite template for Contentful repositories
CSS
2
star
37

jumpgate-app

WIP - Design system app exploration
TypeScript
2
star
38

kramdown-instant-article

A Kramdown converter optimised for Facebook Instant Articles
Ruby
2
star
39

contextual-help

Content type contextual help UI extension
JavaScript
2
star
40

floating-editor-extension

JavaScript
1
star
41

Atom-Exporter

An Atom package for exporting your current workspace (or sub folder of it) into Contentful
CoffeeScript
1
star
42

scheduling_app

Scheduled publishing using Contentful's API and Middleman
Ruby
1
star
43

slack-bot-command-example

Gomix Slack Command Example
JavaScript
1
star
44

contentful_aframevr_middleman

Contentful + AFrame VR + Middleman example
Ruby
1
star
45

duplicate-space-cli

A bash script that will create a new space and duplicate the content model from your reference space.
Shell
1
star