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
}
}
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
}
Then we get something like:
{
"data": {
"person": {
"name": "Jane"
}
}
}
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
}
}
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
}
}
}
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
}
Then we may get something like:
{
"data": {
"person": {
"name": "Jane"
}
}
}
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 iftrue
.@skip(if: Boolean)
— skip this field if the argument istrue
.
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
}
}
Then if we make the following query:
{
"firstName": "Joe",
"lastName": "Smith"
}
we may get the following response:
{
"data": {
"createPerson": {
"firstName": "Joe",
"lastName": "Smith"
}
}
}
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
}
}
}
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 the data returned in the response.
For example, if we have the following query:
{
search(text: "an") {
__typename
... on Human {
name
}
... on Robot {
name
}
}
}
Then we may get the following response from the server:
{
"data": {
"search": [
{
"__typename": "Human",
"name": "Hans"
},
{
"__typename": "Robot",
"name": "Jane"
}
]
}
}
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.