Working with Query Arguments

February 06, 2020


A common refrain you'll hear about GraphQL is that you can make one request and get only the data you want. A query will return just the fields that you request, nothing more and nothing less. For example in the allLifts query, we make a selection for a certain set of fields:

query {
allLifts {
name
status
}
}

Note: To follow along with these queries, go to snowtooth.moonhighway.com.

There are several other fields that are part of the Lift type but not part of this query. We don't have to deal with them in the response though, because we're only requesting name and status.

Even though we're reducing the number of fields in the response, let's consider we only want to display one of these lifts in our UI. If you issued this query from the client, you'd get the full array of data back, and then it'd be the job of some JavaScript on the client to iterate over the array, find the right lift, and then display. Asking the client to transform that data is a lot of extra processing.

GraphQL allows you to move a lot of that logic to the GraphQL server. Instead of parsing, sorting, filtering on the client, you can do this work on the server, and then deliver the exact data that you need to the client.

Our UI only needs the one lift, right? So let's use one of the queries available on the API, the Lift query. To make this possible, we need to use query arguments. First, let's request a specific lift by id:

query {
Lift(id: "panorama") {
name
status
}
}

When we send this query, the GraphQL server will find the object in the lifts array that matches the id for panorama. Just the data that we need. If you look at the underlying schema, you'll notice that the id argument is non-nullable. We have to provide a value. But these types of filters can be optional, as we see in the allLifts query:

query {
allLifts(status: OPEN) {
name
status
capacity
}
}

We can filter the results of the allLifts query by sending the optional status argument. This query will return only the open lifts.

Changing Data with Mutations

So far, we've talked about how to get data, but we haven't talked about you might change data with GraphQL. Queries are for getting data, mutations are for changing data. Just like with queries, the schema lists all of the possible mutation types that are available on the API. Mutations are defined like queries. They have names. They can have selection sets that return object types or scalars. The difference is that mutations perform some sort of a data change that affects the state of your backend data.

Writing a mutation starts with the mutation keyword. Then we'll nest the name of the mutation and any fields we want to return from the mutation, just like we did with the query:

mutation {
setLiftStatus(id: "panorama", status: OPEN) {
name
status
difficulty
}
}

The setLiftStatus mutation takes in the id of the lift that we want to change and the new desired status of that lift. Then the setLiftStatus mutation returns the Lift object that has been changed. That way, we can view all of

Variables

We have changed data by sending new string values as mutation arguments. As an alternative, you can use input variables. Variables replace the static value in the query so that we can pass dynamic values instead. Let's consider our setLiftStatus mutation. Instead of dealing with strings, let's use variable names, which in GraphQL are always preceded by a $ character:

mutation ($id: ID! status: LiftStatus!) {
setLiftStatus(id: $id, status: $status) {
name
status
difficulty
}
}

The static value is replaced by a $variable. Then, we state that the $variable can be accepted by the mutation. From there, we map each of the $variable names with the argument name. In GraphiQL or the Playground, there is a window for Query Variables. This is where we send the input data as a JSON object. Be sure to use the correct variable name as the JSON key:

{
"id": "panorama",
"status": "CLOSED"
}

One quick note here that when we pass an enum value inline with the query we don't use quotes around the value:

mutation {
setLiftStatus(id: "panorama", status: OPEN) {
name
status
difficulty
}
}

It's OPEN not "open". When we pass the values as variables with JSON, we have to use valid JSON syntax and surround each word with doublequotes.

Variables are very useful when sending argument data. Not only will this keep our mutations more organized in a test, but allowing dynamic inputs will be hugely helpful later when connecting a client interface.

Default Values

It's also possible to supply a default value for a query or mutation argument. We'll construct the trailCount query to demonstrate:

query($status: TrailStatus!) {
trailCount(status: $status)
}

The trailCount query takes in status as an argument. We're using query variables here too. But what happens if I don't supply a JSON variable value in the Query Variables panel? We'll get an error: Response not successful: Received status code 400. To resolve this, we could just update the JSON panel, or we could provide a default value for status using an equals sign:

query($status: TrailStatus = OPEN) {
trailCount(status: $status)
}

Note that you'll need to remove the exclamation point from TrailStatus. By setting TrailStatus to OPEN, this means that there will never be a case where we supply null. Instead, if we don't supply a value, the mutation will execute using the default value.