GraphQL: The Query Language

Kaustubh Vyas
Globant
Published in
9 min readJan 8, 2021

I believe you have seen some interesting aspects of using GraphQL and that’s why you want to dig into it a little deeper.

If not, you can have an architectural overview of GraphQL in an article about Introduction: Overview of GraphQL.

By the end of this article, you will get a fair idea about how to use GraphQL in front-end apps. We will play around with GraphQL queries with some examples and using GitHub APIs.

  1. GitHub REST APIs
  2. GitHub GraphQL APIs

Let’s see how GraphQL makes sense against the real time API implementations.

Getting started: Real Quick

To save you from all the setup just to explore GraphQL, we have a perfect place where we can play around the query language and it’s features as much as you want.

All you need is:

  1. Get GraphiQL downloaded and installed on your system.
  2. Create a “Personal Access Token” for your GitHub account.
  3. Configure the ‘Authorization’ header in GraphiQL with the created token.
  4. Configure Server Endpoint with “https://api.github.com/graphql”.
  5. Go to GitHub GraphQL APIs to explore the documentation.

OR

  1. Authorize GitHub APIs to use your account data.
  2. Go to the following link:

https://developer.github.com/v4/explorer/

Here, you have a ready-made setup of a GraphQL server, with plenty of data as well as full API Documentation. You can explore the GraphQL as much as you want here. This tool is called as GraphiQL. It’s a GraphQL IDE equivalent to other API Doc tools like Swagger.

While we explore the GraphQL there, let’s understand some basic concepts of GraphQL so that it will help you modify the query and play around with it.

Building blocks of GraphQL Query
Assume you want to fetch user’s details from a server. If server is providing REST APIs, you would have consumed an API endpoint like:

GET /user/john-doe/

In GraphQL, to fetch the same data, a typical GraphQL query looks like this:

query GetUser {
user(login: "john-doe") {
login
id
avatarUrl
...
}
}

A GraphQL query has some basic building blocks from following:

  1. Fields
  2. Arguments
  3. Variables
  4. Schema
  5. Aliases
  6. Fragments
  7. Directives
  8. Mutations
  9. Interfaces
  10. Unions

1. Fields

Let’s assume we want to fetch user’s details from the server. We want to display only name and avatar_url from it.

In REST, we may have an API endpoint like GET /user/:user_id. Which will return following data:

The response is huge. Isn’t it? We only wanted to know the user’s name and avatar_url. But in the above response, there are way too many extra fields, which we don’t need. But in REST, we usually don’t have an option to choose only those required fields, out-of-the box.

Here is where GraphQL fields come to use. In GraphQL, you will have the flexibility to specify the fields for which you want to have the data. You can fetch the required information from the server via GraphQL through the following query:

Request:
{
github {
user(login: “john-doe”) {
name
avatarUrl
}
}
}

This will return the following response data:

Response:
{
"data": {
"github": {
"user": {
"name": "John Doe",
"avatarUrl":
"https://avatars1.githubusercontent.com/u/XXXXXXXX?v=4"
}
}
}
}

Suppose, if you also want to fetch the user’s GitHub repositories info, then you will have to make another call to API endpoint GET /user/:user_id/repos separately.

But in case of GraphQL, you can fetch both details in a single API call through following GraphQL query:

Request:
{
user(login: "john-doe") {
login
id
avatarUrl
repositories(first: 5) {
totalCount
nodes {
id
name
}
}
}
}

Which will return you the following data:

Response:
{
"data": {
"user": {
"login": "john-doe",
"id": "MDQ6VXNlcjE3MDQxNzY=",
"avatarUrl": "https://avatars0.githubusercontent.com/u/1704176?v=4",
"repositories": {
"totalCount": 1,
"nodes": [
{
"id": "MDEwOlJlcG9zaXRvcnk0MjE5ODgz",
"name": "my-first-app"
}
]
}
}
}
}

From the above set of example, we can say that the ability to choose fields in the responses while querying the data from the backend, is the most useful feature of GraphQL, which makes front-end team’s life easier.

GraphQL fields have different data types. Let’s understand these various data types that GraphQL has. This will help us to have a better understanding of the GraphQL query language.

  1. Root Field:
    The Root Field defines the Service from which you want to fetch the data. You can specify more than one services such as github, reddit etc. This way, GraphQL comes in handy while integrating the application with multiple services.
  2. Scalar Field:
    The Scalar Fields are the fields which have Raw Data Type. E.g. String, Int, Boolean etc.
  3. Complex Field:
    The Complex Fields are the fields which have Data Types derived by some predefined Schema. E.g. User, which has multiple properties like id, name, avatar_url etc, defined by User schema.

2. Arguments

Since we know now which service is to be used and which fields are to be fetched, you can create a sample query. In the above query, we have mentioned the User ID “john-doe”, which acts as an argument for the parameter username.

3. Variables

In most of the applications, these variables will be dynamic. To make the username dynamic, we will write the above query as follows:

query GetGitHubUser($input: String!) {
user(login: $input) {
login
id
avatarUrl
repositories(first: 5) {
totalCount
nodes {
id
name
}
}
}
}

Notice the keyword String. This way we are defining the Data Type of the variable named input. Also, the exclamatory mark (!) defines that argument as required.

4. Schema

GraphQL is a typed language. The Server defines the data types of the data which it returns. That data type is defined in terms of Schema.

In our example, the User entity which we are fetching has a defined type at the Server. Each of it’s properties are declared and given a type in the Schema.

Also, notice the repos field also has it’s own data type. The User Schema there can be defined as following:

type GithubUser {
login: String
id: Int
avatar_url: String
repos: [GithubRepo]
}

So here, repos field has type of GithubRepo Schema which can be defined as:

type GithubRepo  {
id: Int
name: String
commits(limit: Int): [GithubCommit]
owner: GithubUser
}

Here, each GithubRepo has an owner which is of type GithubUser and an array of commits which has a type of GithubCommit.

The Schema works as an API Doc for the entities. You can see all these Schemas in the Documentation Explorer of the GraphiQL.

E.g. The SchemaGithubCommitspecifies that the field commits takes an argument named limit to limit the number of commits to be returned.

5. Aliases

You must have noticed that the response data contains the property names exactly equal to the field names specified in the query. Actually, it’s the other way around. We are mentioning the field names equal to the property names specified in the Schema of that entity.

If we want to name these as per our choice, we use the concept of Aliases. You can specify some other name for the same property using an Alias, as follows:

query GetGitHubUser($input: String!) {
user(login: $input) {
username: login
userId: id
profileImage: avatarUrl
repositories(first: 5) {
totalRepositories: totalCount
data: nodes {
repositoryId: id
repositoryName: name
}
}
}
}

This will give you the following result:

{
"data": {
"user": {
"username": "john-doe",
"userId": "MDQ6VXNlcjE3MDQxNzY=",
"profileImage": "https://avatars0.githubusercontent.com/u/1704176?v=4",
"repositories": {
"totalRepositories": 1,
"data": [
{
"repositoryId": "MDEwOlJlcG9zaXRvcnk0MjE5ODgz",
"repositoryName": "my-first-app"
}
]
}
}
}
}

This comes handy when we want to query the same entity multiple times in a single query. E.g.

query GetRepositories($input: String!) {
user(login: $input) {
username: login
userId: id
profileImage: avatarUrl
ownRepositories: repositories(first: 5, isFork: false) {
totalRepositories: totalCount
data: nodes {
repositoryId: id
repositoryName: name
}
}
forkedRepositories: repositories(first: 5, isFork: true) {
totalRepositories: totalCount
data: nodes {
repositoryId: id
repositoryName: name
}
}
}
}

6. Fragments

In above example, we have repeated the fields for owner and contributor. We may need to repeat the some fields multiple times based on the requirement of our application.

To help us with this repetition, GraphQL provides reusable units called Fragments.

Fragments let you construct sets of fields, and then include them in queries where you need to. Here’s an example of how you could solve the above situation using fragments:

query GetOwnerAndContributor($input: String!) {
user(login: $input) {
username: login
userId: id
profileImage: avatarUrl
ownRepositories: repositories(first: 5, isFork: false) {
...repositoryFields
}
forkedRepositories: repositories(first: 5, isFork: true) {
...repositoryFields
}
}
}
fragment repositoryFields on RepositoryConnection {
totalRepositories: totalCount
data: nodes {
repositoryId: id
repositoryName: name
}
}

Now, we can reuse the repositoryFields fragment wherever we need. The Fragments can also access the variables within the scope of where they are being used.

7. Directives

We discussed how Fragments help us in reusing the query components. Now let’s discuss about the conditional behaviour that we can give to our queries.

We have written a query to fetch user details including the repositories. What if we want to fetch repositories only at some places?

Here, Directives come into picture. A directive can be attached to a field or fragment inclusion, and can affect execution of the query in a way the client would want the server to execute and build the desired response.

There are two Directives supported by the core GraphQL specifications:

  • @include(if: Boolean) Only include this field in the result if the argument is true.
  • @skip(if: Boolean) Skip this field if the argument is true.
query GetGitHubUser($withRepos: Boolean!) {
user(login: "john-doe") {
login
id
avatarUrl
repositories (first: 5) @include(if: $withRepos) {
totalCount
nodes {
id
name
}
}
}
}
------------------------------------------------------
Variables:

{
"withRepos": true
}

Server can define it’s own Directives to add custom behaviours.

I would say this is enough for you to know that how GraphQL is going to help you understand how to use GraphQL to fetch the data.

Let’s see how to modify the data using GraphQL.

Mutations

Mutations are used to modify the data on Server. Mutations serve the similar purpose which by convention POST, PUT, DELETE etc methods of RESTful APIs provide.

Just like in queries, if the mutation field returns an object type, you can ask for nested fields. This can be useful for fetching the new state of an object after an update. Let’s look at a simple example mutation:

The following Mutation query adds a Star to the specified repository:

mutation addStar($input: AddStarInput!) {
addStar(input: $input) {
clientMutationId
starrable {
id
stargazerCount
}
}
}

The following Mutation query removes a Star from the specified repository:

mutation removeStar($input: RemoveStarInput!) {
removeStar(input: $input) {
clientMutationId
starrable {
id
stargazerCount
}
}
}

Interfaces

Being a strictly typed language, GraphQL supports interfaces. An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.

Let’s create a custom Interface here:

interface IMovie {
id: ID!
name: String!
duration: String!
casts: [Character]
rating: Int
}

Implementing this Interface in any Entity Type means that entity needs to have these exact fields, with these arguments and return types.

If we create a type Movie which implements the Interface IMovie it will be as follows:

type Movie implements IMovie {
id: ID!
name: String!
duration: String!
casts: [Character]
rating: Int
}

Unions

A Union is a type of object. But, while selecting fields from a union, we need to specify which type are we fetching from the types specified in union.

For example, let’s create a union SearchResult which will contain the data from either list of WebSeries or list of Movie. Here, we will declare the union as follows:

union SearchResult = WebSeries | Movie

While fetching this data, we will have to specify based on the type of object, which fields are to be included in the response, sent by the server:

query GetVideos($searchText: String!) {
search(text: $searchText) {
__typename
... on WebSeries {
name
noOfSeasons
noOfEpisodes
}
... on Movie {
name
duration
}
}
}

The __typename field resolves to a String which lets you differentiate different data types from each other on the client.

So the above query will give you following result.

{
"data": {
"search": [
{
"__typename": "Movie",
"name": "The Matrix",
"duration": "2h 30m"
},
{
"__typename": "WebSeries",
"name": "The Big Band Theory",
"noOfSeasons": 12,
"noOfEpisodes": 279
}
]
}
}

That’s it. You know all the basics of GraphQL now. You are Good to go one step ahead i.e. Implementing the GraphQL Back-End Server.

Go to Play it your way: POC on GraphQL article. There you will find a step by step guide to create a GraphQL server, with GraphiQL UI.

I hope this helped you to understand the GraphQL: The Query Language.

Thank you.

--

--