Introduction to GraphQL — Variables and Complex Operations

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

GraphQL is a query language for our API and a server-side runtime for running queries by using a type system for our data.

In this article, we’ll look at more complex GraphQL operations, including passing in variables, directives, mutations, and more.

Variables

In most apps, the arguments for queries need to be dynamic. It wouldn’t be a good idea to pass in dynamic arguments directly to the query string.

Fortunately, GraphQL lets us pass in variables into our operations request.

For example, we can write:

query PersonName($id: Int) {  
  person(id: $id) {  
    name  
  }  
}
Enter fullscreen mode Exit fullscreen mode

to pass in an $id variable into our person query.

Then we can pass in variables via an object to make the request:

{  
  "id": 1000  
}
Enter fullscreen mode Exit fullscreen mode

Then we get something like:

{  
  "data": {  
    "person": {  
      "name": "Jane"  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

as the response.

Variable definitions

Variable definitions start with the variable name prefixed with a $, then the type of the variable.

In the example above, we have $id as the variable name and Int as the type.

All declared variables must be either a scalar, enums or other input object types.

If we want to pass a complex object into a field, we need to know the input type to match on the server.

Default variables

We can set default values for variables as follows:

query PersonName($id: Int = 1) {  
  person(id: $id) {  
    name  
  }  
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we added = 1 after $id: Int to set the default value for $id to 1.

Directives

We can use directives to dynamically change the structure and shape of our queries using variables.

For example, we can include directives as follows to make our query dynamic:

query Person($id: Int, $withFriends: Boolean!) {  
  person(id: $id) {  
    name  
    friends @include(if: $withFriends) {  
      name  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the @include(if: $withFriends) directive to conditionally includes friends if $withFriends is true.

Therefore, if we make the following query with the following variables:

{  
  "id": 1000,  
  "withFriends": false  
}
Enter fullscreen mode Exit fullscreen mode

Then we may get something like:

{  
  "data": {  
    "person": {  
      "name": "Jane"  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

as a response.

A directive can be attached to a field or fragment inclusion and can affect the execution of the query in any way the server desires.

The core GraphQL specification includes 2 directives that must be supported by any spec-compliant GraphQL server implementation:

  • @include(if: Boolean) — only include this field if the argument if true.
  • @skip(if: Boolean) — skip this field if the argument is true.

It helps us get out of situations where we would have to do string manipulating to add and remove fields in our query.

Mutations

We can use mutations to send requests to change data on the server.

They’re similar to queries, except that it starts with the keyword mutation instead.

For example, we can define a mutation as follows:

mutation CreatePerson($firstName: String, $lastName: String) {  
  createPerson(firstName: $firstName, lastName: $lastName) {  
    firstName  
    lastName  
  }  
}
Enter fullscreen mode Exit fullscreen mode

Then if we make the following query:

{  
  "firstName": "Joe",  
  "lastName": "Smith"  
}
Enter fullscreen mode Exit fullscreen mode

we may get the following response:

{  
  "data": {  
    "createPerson": {  
      "firstName": "Joe",  
      "lastName": "Smith"  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

In the response, we return the firstName and lastName fields as we specified in the request.

A mutation can contain multiple fields like a query. Mutations fields run in series.

This means that if we sent 2 createPerson mutations in one request. The first will finish before the second begins.

Inline Fragments

We can define interface and union types with GraphQL requests.

To do this, we use inline fragments to access data on the underlying concrete type.

For example, we can write the following query:

query Thing($id: Int!) {  
  person(id: $id) {  
    name  
    ... on Person{  
      gender  
    }  
    ... on Robot{  
      memory  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

In the query above, the ...on operator indicates that we include an inline fragment within our query, where Person and Robot are our fragments.

Named fragments can also be inline fragments since they have a type attached.

Meta fields

We can request a __typename field to get the type of data returned in the response.

For example, if we have the following query:

{  
  search(text: "an") {  
    __typename  
    ... on Human {  
      name  
    }  
    ... on Robot {  
      name  
    }  
  }  
}
Enter fullscreen mode Exit fullscreen mode

Then we may get the following response from the server:

{  
  "data": {  
    "search": [  
      {  
        "__typename": "Human",  
        "name": "Hans"  
      },  
      {  
        "__typename": "Robot",  
        "name": "Jane"  
      }  
    ]  
  }  
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

We can define mutations to make requests to change data.

To make requests dynamic, we can use variables to include variable data and directives for conditional inclusion of data in our response.

We can also use inline fragments for union types and include the type of data returned in the response by including the __typename property.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .