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.