Intro to GraphQL
This guide will be most useful for developers that are new to working with GraphQL. This guide isn’t a comprehensive deep dive into GraphQL but should help you get a basic understanding of it and provide you with resources to dive deeper.
GraphQL stands for Graph Query Language. Graph, meaning “Application Data Graph”. So, GraphQL is is a specification for a Query Language for interacting with API data as a data graph.
GraphQL isn’t tied to any specific database or back-end technology, instead GraphQL can be used with existing code and data. In the case of WPGraphQL, that existing code and data is WordPress.
The “hello world” of GraphQL Queries might look something like the following:
And in response, one might get the following data:
The request looks much like JSON keys, without the values. And the response is JSON with keys and values, and the data returned matches the shape of the request.
The request asked for the field “hello” and the response provided data with the key and value of the “hello” field.
For a “hello world” Schema, one could define a Root Query Type in the Schema with a “hello” field, and declare that the “hello” field will return a value as a string:
GraphQL makes no prescription over the shape of the API. There must be a root Query type that has at least one field, and beyond that, the Schema can be designed however the implementor sees fit. It could be as basic as one “hello” field, but in almost all cases it will be more complex and have fields that return values beyond
Additional Types can be defined and referenced by fields in the Schema. For example:
Here, the field: “viewer” was added to the RootQuery Type and is set to return the type User. The “User” Type is defined to have 2 fields, “id” and “name”.With this Schema, one could make a query like the following:
Because “me” is a field on RootQuery, we can ask for it in our Query. And because “me” returns the “User” type and the “User” type has fields “id” and “name”, we can ask for those fields as selections on the “me” field.If the server had resolvers configured for these fields, we might get a response like the following:
One thing you might have noticed is that the “User” type had an “id” field and a “name” field, but the query only asked for the “name” field. This is because with GraphQL, you get to specify exactly what you want, and the server responds with just what you asked for.As Eve Porcello’s brother describes it, the GraphQL Schema is like a restaurant menu, and a GraphQL query is like the order.
The GraphQL Schema describes what’s possible to be asked for. The Query asks for specific fields that are needed. Much like a server at a restaurant takes your order, goes to the kitchen and brings back the specific items you ordered, the GraphQL server takes the Query and returns data matching the fields that were requested.
The following sections will introduce some concepts of GraphQL and provide resources to learn more about them.
GraphQL can be implemented in many programming languages as an API for any back-end. WPGraphQL is just one implementation of GraphQL using WordPress as the application and data layer.
There are many things that the GraphQL specification doesn’t prescribe, such as Schema design, fetching data behind the scenes, and more. These decisions are made by each individual implementation of a GraphQL server. To learn more about specific concepts of the WPGraphQL implementation, you can read about WPGraphQL Concepts page.
Because GraphQL can be implemented in many ways, the following sections will attempt to speak to general GraphQL concepts that apply to any GraphQL implementation.
The following sections will introduce you to features of the GraphQL Query language and how to compose Queries and Mutations and the features available when interacting with a GraphQL API. This section is more relevant to readers that want to learn more about interacting with GraphQL APIs.
Queries and Mutations are the primary operations that can be made against a GraphQL server.
Queries allow users to ask for data, while Mutations allow for data to be changed.
Technically, GraphQL doesn’t enforce this, but it’s best practice. It is possible to have Mutations that don’t change underlying data or Queries that do change underlying data, but in general, if the intent of an operation is to change data, it should be in the GraphQL Schema as a Mutation.
In this section, we’ll break down various features of the GraphQL Query Language. After reading this, you should feel comfortable interacting with just about any GraphQL API you come across.
Fields are at the heart of the GraphQL Query Language. The most basic of GraphQL operations will include fields.
Let’s say we had a GraphQL API that supported the following Query:
And returned the following response:
As you can see from the example, when we ask for a field in a GraphQL Query, the response has the same shape as the Query, and the server knows exactly what fields the client is asking for.
Fields in a GraphQL Schema declare a
Type of data the field will return.
In this case, the field named
hello was defined to return a String, and as we can see the value returned is
Fields can be defined to return more complex Types as well. Take the following example:
In this query, I’m asking for the field
me, but this returns an Object, so I must select which fields of that object I want as well. In this case, I ask for the field
name and would get something like the following:
Fields can also return Lists. For example, we might make the following Query:
And we might get the following response:
In this example, the
friends field returns an array of items. GraphQL Queries look the same for both single items or lists of items, but you can know which to expect based on what’s indicated in the Schema.
Just asking for fields and getting data back in the same shape is already powerful, but GraphQL also supports arguments, allowing fields to have even more granular control over the data being requested.
Here, we can pass an ID to the field and get a specific
Person in response.
RESTful APIs allow you to pass a single set of arguments with the request, but in GraphQL, every field in nested objects can have their own set of arguments (if the fields support arguments). This can allow a single GraphQL request to replace many round-trip requests.
Arguments can be of many different Types. In the above example, we showed a
Boolean argument that would accept a
false input, but arguments can defined to accept Strings, Integers, Enums, custom Scalars or more complex Input Objects.
Aliases are a powerful feature of GraphQL that allow you to change the name of a field in the response. This can be helpful when asking for the same field with different arguments, as the response needs to return data in the matching shape. If we asked for 2 fields both named
person, GraphQL wouldn’t quite know how to respond. We can alias like so:
And get the following response:
In the above examples we’ve used shorthand syntax to write a query and we’ve omitted the keyword
query and the query name. While this is valid, it can be helpful to name your queries by giving them an operation name.
In this example,
query is the operation type and
GetPersonById is the operation name.
While operation names are not required, they’re best practice. Having an operation name can make it easier to identify queries in your codebase or in server side logs of operations, etc. Additionally, operation names are required if you want to pass variables to a query.
Variables allow for arguments to be dynamic, while allowing the Query to be a static string. For example, there might be a text field that allows a user to input a keyword to search for something.
It’s not a good idea to concatenate the Query string to accomplish this as that would require run-time alterations to the query and could also make it difficult to diagnose when a query is problematic, as all queries would actually be unique queries.
Instead, GraphQL provides a feature called variables.
Let’s take the example above that included an argument, and convert it to use a variable:
Here, we’ve added
($id:Int!) to the operation name, declaring the variable
$id and telling GraphQL that the input will be a required
Then, we replaced
id: 123 with
id: $id, which tells the query to use the
$id variable when fulfilling the request for the
Below is an example of using variables with the
graphql() function provided by WPGraphQL:
A Fragment is a piece of code or logic that can be shared between multiple queries and mutations.
Fragments can be really useful when we we need to use or repeat the same field in multiple queries.
Let’s look at an example of two queries where we need to get header and footer menus:
You will notice that we have to repeat the menu fields in both of the queries .
Let’s create a fragment for image called
MenuFragment using the keyword
fragment. The name
MenuFragment is a custom name that we have given, and choose to call it something else as well.
MenuItemis the type, which should is constant. We will cover below on how we can find this type for our query.
How to find out the type for Fragment?
You can use the Doc section of the Graphiql, and search for the query, and then as shown in the gif below, find the type of the field.
Let’s put this together and try it in GraphiQL
So as you can see that with the help of Fragments we can reuse the same piece of logic in both queries and when needed, we only need to make updates at one place.
The following sections will introduce you to concepts related to GraphQL Schemas and Types. This section is more relevant to readers that are curious about how a GraphQL API server is built and how.
At the heart of GraphQL is a Schema that is made up of Types, and Types consist of fields.
The Schema is the contract between the Client and the Server and exposes what is possible to be asked for, and the client uses the Schema to ask for exactly what it needs.
GraphQL can be implemented in just about any programming language and can work with just about any data source you could think of.
GraphQL is a specification that defines how Schemas should be constructed and how GraphQL operations sent to a GraphQL API should respond. GraphQL makes almost no prescription over the shape of the Schema, other than the Schema must have at least one Type defined and used as the root of the Schema, and that Type must have at least one field. Beyond that, GraphQL Schemas can be designed any way you can think of.
A very basic GraphQL Schema could be defined like so:
The above snippet is using SDL, or Schema Definition Language, which is a programming-language agnostic way to describe GraphQL Schemas and Types.
Here we have one Type, named
Query with one field named
hello that has declared that it will return a String when queried. And the Schema is defined to have the
query field use the defined
If this Schema were implemented, the following query would be a valid query.
In the above snippet the
Query type was defined. This is a GraphQL Object Type which means it’s a Type in the Schema that has fields. Object Types are typically the most common Type in a GraphQL Schema.
Query type we defined a
hello field. Fields are how we define what a GraphQL consumer can ask for from the API. In our case, we defined the
hello field as a
String, which is the contract between the server and the consumers that when the
hello field is asked for, a string will be returned.
String type is a Scalar type, which means that something concrete will be resolved at this field and that there can not be sub-selections made against a field that returns a Scalar Type.
It’s possible to define a field that returns a list. Using the SDL, that might look like so:
This would allow a user to query like so:
And in response they might get:
However, if there was no data to resolve, or the requesting user didn’t have permission to view the data being asked for, the response for the field might be null, like so:
Fields can be defined as non-nullable, which means that the GraphQL API promises to always return a value when the field is asked for. In SDL, the
! represents non-null.
Given this Schema, any query for the field
nonNullString will always return a String and never a null.
It’s important to note, however, that an empty string is still a string.
Fields in GraphQL can have arguments defined, which allows user input to be provided when the field is asked for, and the input can be used to determine how the field should be resolved.
For example, we could define a field like so:
This defines an argument named
name as an input for the field
The argument would be passed to the field’s resolver, and could be used to determine how the field should resolve. For example, the input value could be used to filter a database, or really anything you could think of.
At the root of the GraphQL Schema is a Query Type, but there can also be a Mutation Type.
Query and Mutation types are the same as all other Object Types, the difference is that they are at the root of the Schema and serve as the entry point to the API.
Technically, Mutations are almost identical to Queries too. There’s nothing in the GraphQL spec that prevents Queries from writing data to a data source, and there’s nothing that requires Mutations to write data to an underlying data source, but it’s best practice to design a GraphQL Schema to use Queries for r retrieving data, and Mutations for changing data.
Above, we saw the
String Scalar type, but GraphQL has a few built-in Scalar types:
Int: A signed 32‐bit integer.
Float: A signed double-precision floating-point value.
String: A UTF‐8 character sequence.
ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an
IDsignifies that it is not intended to be human‐readable.
Most GraphQL implementations (including WPGraphQL) provide ways to register custom Scalar types.
For example, you may want to register a `Date` Scalar that behaves similar to a String, but enforces different validation rules.
As of writing this, WPGraphQL doesn’t provide any custom scalars out of the box, but may in the future.
Typically referred to as Enum Types, these are a special kind of Scalar that is limited to a specific set of values.
This allows you to:
- Validate that any arguments of this type are one of the allowed values
- Communicate through the type system that a field will always be one of a finite set of values
Here’s what an enum definition might look like in the GraphQL schema language:
This means that wherever the
Status Type is used in the Schema, it would be expected to be exactly one of the allowed values
An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
For example, if we had a Schema for animals, we might have a
Cat Type, and they might share an
Animal Interface, which would declare common fields, such as
Then each Type (
Dog) could implement the interface, but also define other fields unique to their Type.
Interfaces are useful when you want to return an object or set of objects, but those might be of several different types. For example, given the following Schema:
One could query:
This allows for common fields to be queried together, but for differentiating fields to be asked for only if the return type matches the specified Type.
Union Types are similar to interfaces, in that they allow different Types to be returned, but they don’t require any shared fields between the types.
For example, let’s say a Zoo website allowed users to search. When users searched, they might get results from the websites Blog, or from the dictionary of Animals.
You might define a Union like so:
In this case, a query could be executed like so:
And the results might look like so:
__typename field is a hidden field available to be queried on all Types in the GraphQL Schema. It resolves to a String which lets consumers differentiate between the returned Types.
Input Types allow complex input to be provided to arguments, instead of just Scalars. This is valuable to Mutations where you might want to accept complex input to create an object. It’s also valuable when allowing clients to sort or filter collections of data.
This Schema would enable a Mutation operation like so:
Input Types are similar to Object Types, but Input Types can only be used for input, not for querying. Additionally, while fields of Object Types can have arguments, fields of Input Types cannot. Thus, Input Types and Output Types cannot be shared in the Schema. For example, you could not create one Type and use that Type as an input and as a queryable Type.
This page was intended to serve as an introduction to GraphQL, but there’s a lot more to learn about GraphQL. Below are some resources that should help in learning more about GraphQL:
- GraphQL.org: The official website of GraphQL
- HowToGraphQL.com: A free and open-source tutorial to learn about GraphQL from zero to production
- Zero to GraphQL in 30 Minutes: A video overview of GraphQL
- So, what’s this GraphQL thing I keep hearing about?: A Blog post on FreeCodeCamp.org