March 7, 2017

The Anatomy of a GraphQL Query

Sashko Stubailo

Sashko Stubailo

GraphQL is just entering the mainstream as a new standard for data fetching. There are now a lot of great conversations happening around developments in the technology and new tools being built every day. One of the best parts of GraphQL is that it gives you a great common language with your team to talk about the data available in your API. But how should you talk about the query language and the core technology itself?

Well, it turns out names for almost every concept in the GraphQL language are right there in the GraphQL specification. But the spec is pretty long, so in this post I’ll lay out some of the most important concepts and terms, with concrete examples, so that you can be an expert in talking about GraphQL.

Note: If you’re trying to learn GraphQL, this isn’t the best place to start. First, read through the concepts on the graphql.org docs, then try using GraphQL with the excellent Learn Apollo tutorial, and finally come back here when you want to go deep into technical language.


Basic GraphQL queries

People commonly call everything that hits your GraphQL API server a “query”. But there are a lot of things mixed in there. What do we call a unit of work we’re asking the server to do? It could be a query, a mutation, or a subscription. The word “request” is pretty coupled to the idea of HTTP and the transport. So let’s start by defining some general concepts:

  • GraphQL document: A string written in the GraphQL language that defines one or more operations and fragments.
  • Operation: A single query, mutation, or subscription that can be interpreted by a GraphQL execution engine.

What are the different parts of a basic operation? Let’s look at a very simple example of a GraphQL document.

A simple query and its parts.

This document shows off the main building blocks of GraphQL, which specify the data you’re trying to fetch.

  • Field: A unit of data you are asking for, which ends up as a field in your JSON response data. Note that they are always called “fields”, regardless of how deep in the query they appear. A field on the root of your operation works the same way as one nested deeper in the query.
  • Arguments: A set of key-value pairs attached to a specific field. These are passed into the server-side execution of this field, and affect how it’s resolved. The arguments can be literal values, as in the query above, or variables, as in the examples below. Note that arguments can appear on any field, even fields nested deep in an operation.

The query above is somewhat of a shorthand form of GraphQL, which lets you define a query operation in a very concise way. But there are three optional parts to a GraphQL operation that aren’t used there. You’ll need these new parts if you want to execute something other than a query, or pass dynamic variables.

Here’s an example that includes all of them:

A more detailed query and its parts.
  • Operation type: This is either querymutation, or subscription. It describes what type of operation you’re trying to do. While all of them look similar in the language, they have slightly different modes of execution on a spec-compliant GraphQL server.
  • Operation name: For debugging and server-side logging reasons, it’s useful to give your queries meaningful names. That way, when you see something wrong going on either in your network logs or your GraphQL server (for example in a tool like Apollo Optics), you can easily find that query in your codebase by name instead of trying to decipher the contents. Think of it like a function name in your favorite programming language.
  • Variable definitions: When you send a query to your GraphQL server, you might have some dynamic parts that change between requests, while the actual query document stays the same. These are the variables of your query. Because GraphQL is statically typed, it can actually validate for you that you are passing in the right variables. This is where you declare the types of variables you are planning to provide.

Variables are passed separately from the query document in a transport-specific way, In today’s GraphQL server implementations, that’s usually JSON. Here’s how a variables object might look for the query above:

An example variables object.

You can see that the key here matches the name of the variable defined in the variable definitions, and the name is one of the members of the Episode enum.

  • Variables: The dictionary of values passed along with a GraphQL operation, that provides dynamic parameters to that operation.

There’s one other core concept that’s not often named, but is important when talking about GraphQL in a technical sense — what’s all that stuff between the brackets called?

The selection set is a concept you’ll see very frequently in the GraphQL specification, and it is what gives GraphQL its recursive nature, allowing you to do nested data fetching.

  • Selection set: A set of fields requested in an operation, or nested within another field. A GraphQL query must contain a selection set on any field that returns an object type, and selection sets are not allowed on fields that return scalar types, such as Int or String.

Fragments

GraphQL becomes even more powerful when you introduce fragments. These bring with them a new set of concepts.

  • Fragment definition: Part of a GraphQL document which defines a GraphQL fragment. This is also sometimes called a named fragment, in contrast to an inline fragment which we’ll get to below.
A fragment definition and its parts.
  • Fragment name: The name of each fragment has to be unique within a GraphQL document. This is the name you use to refer to the fragment in an operation or in other fragments. Fragment names can also be useful for server-side logging, much like operation names, so we recommend using explicit and meaningful names. If you name your fragments well, you can track down which part of your code defines that fragment if you want to optimize your data fetching later.
  • Type condition: GraphQL operations always start at the query, mutation, or subscription type in your schema, but fragments can be used in any selection set. So in order to validate a fragment against your schema in isolation, you need to specify which type it can be used on, and that’s where the type condition comes in.

And, just like operations, fragments have a selection set. These work just like the selection sets in operations.

Using fragments in your operations

Fragments aren’t very useful until you use them in an operation. Fragments can appear in two different ways, as we’ll see below:

Using two different kinds of fragments in a query.
  • Fragment spread: When you use a fragment inside an operation or another fragment, you do so by putting ... followed by the fragment name. This is called a fragment spread, and it can appear in any selection set that matches that named fragment’s type condition.
  • Inline fragment: When you just want to execute some fields depending on the type of a result, but you don’t want to split that out into a separate definition, you can use an inline fragment. This works just like a named fragment, but is written as part of your query. One difference about inline fragments is that they aren’t actually required to have a type condition, and can be used with just a directive, as we’ll see in the example below.

Directives

Directives are a way to get additional functionality out of your GraphQL server. Directives shouldn’t affect the value of the results, but do affect which results come back and perhaps how they are executed. They can appear almost anywhere in a query, but in this article we’ll focus only on the skip and include directives that are in the current GraphQL specification.

You probably wouldn’t usually put all of these in one query, but it’s the easiest way to demonstrate.

Above is a kitchen sink of examples of where you can use the skip and include directives. These directives in particular make GraphQL execution conditionally skip fields and omit them from the response. Because the syntax of directives is quite flexible, they can be used to add more features to GraphQL without making parsing or tooling more complicated.

  • Directive: An annotation on a field, fragment, or operation that affects how it is executed or returned.
  • Directive arguments: These work just like field arguments, but they are handled by the execution engine instead of being passed down to the field resolver.

Get involved in the conversation

A big part of the benefit of GraphQL is having a common language to talk about data fetching. Now, you’ll be well-equipped to participate in deep, technical conversations about GraphQL, for example the ongoing discussion about GraphQL subscriptions.

In this article, we’ve only covered part of the GraphQL specification — the part that deals with the query language. In a future post, we might delve into the various terms used to describe a GraphQL schema.

Want to work on GraphQL technology full-time? We’re hiring for a variety of positions including frontend, backend, and open source!

Written by

Sashko Stubailo

Sashko Stubailo

Read more by Sashko Stubailo