GraphQL Query: How to Talk to the GraphQL Server

What is GraphQL query language?

GraphQL query language is a specific syntax and set of rules to query data from a GraphQL API. It provides a structured and efficient way to request and retrieve the desired data from the server.

The GraphQL query language allows clients to specify the exact data they need, making it more flexible compared to traditional RESTful APIs where the server defines the response structure. With GraphQL, clients can send a single request to the server and receive only the requested data, eliminating over-fetching or under-fetching of data.

GraphQL query operations

There are three types of operations that you can execute with GraphQL: queries, mutations, and subscriptions

  • Query – A read-only operation that retrieves data from the server.
  • Mutation – An operation that modifies data on the server.
  • Subscription — A long-lived operation that receives updates from the server when data changes. They typically track real-time data, such as stock prices or social media updates.

Syntax

Each operation consists of three parts:

  • Operation type – The type of operation, which is either query, mutation, or subscription.
  • Operation name – An optional name for the operation.
  • Fields – A list of fields that specify the data you want to retrieve, modify, or receive updates for.
GraphQL query operations

Examples:

Query:

query {
  user(id: 1234) {
    name
    age
  }
}

Mutation:

mutation {
  createUser(name: "John Doe", age: 30) {
    id
    name
    age
  }
}

Subscription:

subscription {
  newPosts {
    id
    title
    body
  }
}

GraphQL Query Elements

Now that you have gained some insight into what GraphQL query operations are, let us see the elements that form these operations. in this post, I am going to talk about fields, arguments, aliases, fragments, variables, and directives. with the knowledge of these elements, you can execute simple to complex query operations to communicate with the GraphQL server.

Fields 

Fields represent the specific data elements or properties that clients want to retrieve from the server. Fields can have arguments to filter, sort, or paginate the data.

Field can also have reference to other Objects. When you have such as reference, you can select the field of the referenced Object as well.

For example, the query below uses the Author Object. This query read two fields of the Author object: name and books. name is a string, however, the books is an Object. It references a Book Object. Therefore, you can access the fields in the Book object as well. Here, the “title” is a field in the Book object.

query Author{
  authors {
    name
    books {
      title
    }  
 }}

When you run queries (and mutation, Subscriptions ) you can pass arguments to a field. This is useful in filtering data.

For example, if you want a specific author you can pass as an argument to the “id” field. ( And of course, to work like this with an argument, this query must be defined in the schema definition )

query{
  author(id:"A2") {
    age
    books {
      title
    }
  }
}

You can pass arguments into any field, including the fields of nested Objects. The following example will show only two books written by the author with the id “A2”.

query{
  author(id:"A2") {
    age
    books(limit: 2) {
      title
    }
  }
}

Aliases

Let’s say I want to know about books written by two authors with their IDs “A2” and “A3”. I should run the query as follows:

query{
  author(id:"A3") {
    age
    books {
      title
    }
  }

  author(id:"A2") {
    age
    books {
      title
    }
  }
}

In GraphQL, however, you can not do so. I will produce an execution error below:

"message": "Fields \"author\" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional."

This is because we use two different arguments for the same field, which causes a conflict.

The error also tells us to use aliases. Now, let’s see how to use aliases. Aliases are basically identities that you use for these queries. Since there are only two queries, you can use only one alias to differentiate two queries. But, I prefer to use two aliases to improve readability.

QueryData requested
query{
  Author1:author(id:”A3″) {
    age
    books {
      title
    }
  }


  Author2: author(id:”A2″) {
    age
    books {
      title
    }
  }
}
{
  “data”: {
    “Author1”: {
      “books”: [
        {
          “title”: “1Q84”
        },
        {
          “title”: “Norwegian Wood”
        }
      ]
    },
    “Author2”: {
      “books”: [
        {
          “title”: “1Q84”
        },
        {
          “title”: “Norwegian Wood”
        }
      ]
    }
  }
}

Fragments

Fragments are reusable components used with queries, mutations, or subscriptions.

Syntax:

fragment FragmentName on TypeName {
  field1
  field2
  # ...
}
  • FragmentName is the name you give to the fragment.
  • TypeName is the name of the type the fragment applies to. The type can be an object, interface, union, or scalar type.
  • field1, field2, etc. are the fields you want to include in the fragment’s selection set.

Take the following query for example in the table below.

As you can be noticed in the query on the left side column of the table, the name, age, and books fields repeat in both Author1 and Author2. You can put these repeating fields of the Author object into a fragment and use that fragment instead. The right side column of the table shows how it has been done using fragments. 
Note: The spread operator (…) is used to include the fields from the fragment at that location.

With no fragmenstsWith Fragments
query{
  Author1:author(id:"A3") {
    name
    age
    books(limit: 1){
      title
    }
  }


  Author2: author(id:"A2") {
    name
    age
    books(limit: 1){
      title
    }
  }
}

query{
  Author1:author(id:"A3") {
    ...authorDetails
    
  }




  Author2: author(id:"A3") {
    ...authorDetails
    
  }
  
}




fragment authorDetails on Author{
  name
  age
  books(limit: 1){
    title
  }
}

Variables

When you use GraphQL queries, you can pass dynamic data into these queries. As you might have noticed before, we used arguments with fields. So far, we used static values in these arguments to filter and fetch the data. But, in a real-world application, these arguments hold dynamic values that come from the user input. Therefore, in GraphQL queries, you can replace these static values with variables to hold dynamic values coming from user input or other sources.

In a GraphQL query, variables are defined with the syntax $variableName: VariableType. The variable name can be any valid name, and the variable type must match the corresponding type defined in the schema.

ex:

query GetAuthor($authorId: ID!) {
  author(id: $authorId) {
    name
    age
  }
}

In the above query, $authorId is a variable of type ID! (non-null ID).

When executing the query, you need to provide the values for the variables separately. This can be done through variables input:

{
  "authorId": "A1"
}

You can send this JSON object as the variables input when making the GraphQL query.

In many GraphQL playgrounds, there is a separate section that let you add JSON objects so that you can test these GraphQL operations.

You can create complex GraphQL operations using more than one variable and /or using variables inside fragments. This not only makes queries dynamic, but also reusable.

You can also provide default values to variables using the syntax:

$variableName: VariableType = defaultValue

Note, I have provided one for the $limit variable.

query getAuthor( $authorId_1: ID!, $authorId_2: ID!, $limit:Int = 1 ){
  Author1:author(id: $authorId_1 ) {
    ...authorDetails    
  }

  Author2: author(id: $authorId_2) {
    ...authorDetails    
  }  
}

fragment authorDetails on Author{
  name
  age
  books(limit: $limit){
    title
  }
}

The JSON object that you need to pass to run this query.

{
  "authorId_1": "A1",
  "authorId_2": "A2",
  "limit":2
}

Directives

GraphQL allows you to conditionally add or remove a field using directives. There are two main directives you can use in GraphQL query language: @include and @skip.

@include@skip
conditionally include a field in the response based on a Boolean condition.

can specify the condition using a variable or a hardcoded Boolean value. 

If it is true, the field will be included in the response.

If it is false,  the field will be omitted from the response
conditionally include a field in the response based on a Boolean condition.

can specify the condition using a variable or a hardcoded Boolean value. 

If it is true, the field will be included in the response.

If it is false,  the field will be omitted from the response

Ex:

Take a look at the following sample dataset.

const authors = [
  { id: "A1", name: "J.K. Rowling" },
  { id: "A2", name: "Stephen King",age : 75 },
  { id: "A3", name: "Haruki Murakami",age:74 },
];

Here, the age will be only if the condition is true.

Query with directiveResponse
query {
  authors {
    name
    age@include( if : true )
  }
}

{
  "data": {
    "authors": [
      {
        "name": "J.K. Rowling",
        "age": null
      },
      {
        "name": "Stephen King",
        "age": 75
      },
      {
        "name": "Haruki Murakami",
        "age": 74
      }
    ]
  }
}
query {
  authors {
    name
    age@include( if : false)
  }
}

{
  "data": {
    "authors": [
      {
        "name": "J.K. Rowling"
      },
      {
        "name": "Stephen King"
      },
      {
        "name": "Haruki Murakami"
      }
    ]
  }
}

If you use variables, this will be dynamic and reusable. Note, I have also assigned a default value for the $displayAge variable. This can come in handy if the user does not provide a value for $displayAge. 

query ( $displayAge : Boolean ! = false) {
  authors {
    name
    age@include( if : $displayAge )
  }
}

A sample data in a JSON object

{
  "displayAge": true
}

Note: it is crucial to understand that the @include and @skip directives DO NOT check the values of the fields they are applied to. They simply determine whether or not the field should be included in the result. Therefore, even if the “age” is not assigned a value or “age” is not a property of the data source, the directives do not validate this.

Wrapping up

In this post, we explore the basics of GraphQL query language. You can execute three query operations with GraphQL query language: query, mutation, and subscription. These query operations include fields, arguments, aliases, fragments, variables, and directives. Fields represent the specific data elements or properties. Aliases allow you to assign alternative names to fields in a query. Fragments are reusable units that you can use in a query. Variables make your query operations dynamic, and directives allow you to change the structure of a query operation.

Resources

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top