Querying

Fields

At its simplest, GraphQL is about asking for specific fields on objects. Let's start by looking at a very simple query and the result we get when we run it:

Request

Response

query {
countries {
name
}
}
{
"data": {
"countries": [
{
"name": "Andorra"
}
]
}
}
query {
countries {
# Queries can have comments!
name
states {
name
}
}
}
{
"data": {
"countries": [
{
"name": "Argentina",
"states": [
{
"name": "Ciudad Autónoma de Buenos Aires"
},
{
"name": "Buenos Aires"
}
]
}
]
}
}
⚠️

Oh, one more thing - the query above is interactive. That means you can change it as you like and see the new result.

Arguments

If the only thing we could do was traverse objects and their fields, GraphQL would already be a very useful language for data fetching. But when you add the ability to pass arguments to fields, things get much more interesting. In a system like REST, you can only pass a single set of arguments - the query parameters and URL segments in your request. But in GraphQL, every field and nested object can get its own set of arguments, making GraphQL a complete replacement for making multiple API fetches. You can even pass arguments into scalar fields, to implement data transformations once on the server, instead of on every client separately.

Request

Response

query {
country(code: "AR") {
code
name
states {
name
}
}
}
{
"data": {
"country": {
"code": "AR",
"name": "Argentina",
"states": [
{
"name": "Ciudad Autónoma de Buenos Aires"
},
{
"name": "Buenos Aires"
}
]
}
}
}
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 5.6430448
}
}
}

Arguments can be of many different types. In the above example, we have used an Enumeration type, which represents one of a finite set of options (in this case, units of length, either METER or FOOT). GraphQL comes with a default set of types, but a GraphQL server can also declare its own custom types, as long as they can be serialized into your transport format.

Aliases

If you have a sharp eye, you may have noticed that, since the result object fields match the name of the field in the query but don't include arguments, you can't directly query for the same field with different arguments. That's why you need aliases - they let you rename the result of a field to anything you want.

Request

Response

query {
bestCountry: country(code: "AR") {
code
name
states {
name
}
}
secondBestCountry: country(code: "IT") {
code
name
states {
name
}
}
}
{
"data": {
"bestCountry": {
"code": "AR",
"name": "Argentina",
"states": [
{
"name": "Ciudad Autónoma de Buenos Aires"
},
{
"name": "Buenos Aires"
}
]
},
"secondBestCountry": {
"code": "IT",
"name": "Italy",
"states": []
}
}
}

In the above example, the two hero fields would have conflicted, but since we can alias them to different names, we can get both results in one request.

Fragments

Let's say we had a relatively complicated page in our app, which lets us look at two heroes side by side, along with their friends. You can imagine that such a query could quickly get complicated, because we would need to repeat the fields at least once - one for each side of the comparison.

That's why GraphQL includes reusable units called fragments. Fragments let you construct sets of fields, and then include them in queries where you need to. Here's an example of how you could solve the above situation using fragments:

Request

Response

query {
bestCountry: country(code: "AR") {
...countryFields
}
secondBestCountry: country(code: "IT") {
...countryFields
}
}
fragment countryFields on Country{
code
name
states {
name
}
}
{
"data": {
"bestCountry": {
"code": "AR",
"name": "Argentina",
"states": [
{
"name": "Ciudad Autónoma de Buenos Aires"
},
{
"name": "Buenos Aires"
}
]
},
"secondBestCountry": {
"code": "IT",
"name": "Italy",
"states": []
}
}
}

Operation name

The operation type is either query, mutation, or subscription and describes what type of operation you're intending to do. The operation type is required unless you're using the query shorthand syntax, in which case you can't supply a name or variable definitions for your operation.

The operation name is a meaningful and explicit name for your operation. It is only required in multi-operation documents, but its use is encouraged because it is very helpful for debugging and server-side logging. When something goes wrong (you see errors either in your network logs, or in the logs of your GraphQL server) it is easier to identify a query in your codebase by name instead of trying to decipher the contents. Think of this just like a function name in your favorite programming language. For example, in JavaScript we can easily work only with anonymous functions, but when we give a function a name, it's easier to track it down, debug our code, and log when it's called. In the same way, GraphQL query and mutation names, along with fragment names, can be a useful debugging tool on the server side to identify different GraphQL requests.

Request

Response

query getCountryByCode {
bestCountry: country(code: "AR") {
code
name
states {
name
}
}
}
{
"data": {
"bestCountry": {
"code": "AR",
"name": "Argentina",
"states": [
{
"name": "Ciudad Autónoma de Buenos Aires"
},
{
"name": "Buenos Aires"
}
]
},
}
}

Variables

So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic: For example, there might be a dropdown that lets you select which Star Wars episode you are interested in, or a search field, or a set of filters.

It wouldn't be a good idea to pass these dynamic arguments directly in the query string, because then our client-side code would need to dynamically manipulate the query string at runtime, and serialize it into a GraphQL-specific format. Instead, GraphQL has a first-class way to factor dynamic values out of the query, and pass them as a separate dictionary. These values are called variables.

When we start working with variables, we need to do three things:

  1. Replace the static value in the query with $variableName
  2. Declare $variableName as one of the variables accepted by the query
  3. Pass variableName: value in the separate, transport-specific (usually JSON) variables dictionary

Here's what it looks like all together:

Request

Response

query getCountryByCode($countryCode: ID!) {
bestCountry: country(code: $countryCode) {
code
name
states {
name
}
}
}
{
"countryCode": "AR"
}
{
"data": {
"bestCountry": {
"code": "AR",
"name": "Argentina",
"states": [
{
"name": "Ciudad Autónoma de Buenos Aires"
},
{
"name": "Buenos Aires"
}
]
},
}
}

Default variables

Default values can also be assigned to the variables in the query by adding the default value after the type declaration. When default values are provided for all variables, you can call the query without passing any variables. If any variables are passed as part of the variables dictionary, they will override the defaults.

Directives

We discussed above how variables enable us to avoid doing manual string interpolation to construct dynamic queries. Passing variables in arguments solves a pretty big class of these problems, but we might also need a way to dynamically change the structure and shape of our queries using variables. For example, we can imagine a UI component that has a summarized and detailed view, where one includes more fields than the other.

Let's construct a query for such a component:

Request

Response

query getCountryByCode($countryCode: ID!, $withStates: Boolean!) {
bestCountry: country(code: $countryCode) {
code
name
states @include(if: $withStates){
name
}
}
}
{
"countryCode": "AR",
"withStates": false
}
{
"data": {
"bestCountry": {
"code": "AR",
"name": "Argentina"
}
}
}