Graphql In Action
Exploring Graphql Apis
This is still a work in progress. New content is synced here as it gets ready.

Exploring GraphQL APIs

This chapter covers

  • Using GraphQL’s in-browser IDE to test GraphQL requests

  • Exploring the fundamentals of sending GraphQL data requests

  • Exploring read and write example operations from the GitHub GraphQL API

  • Exploring GraphQL’s introspective features

The easiest way to start learning about the powerful features of the GraphQL language is to use its feature-rich interactive in-browser IDE. This IDE uses GraphQL’s type system to provide features you can use to explore what you can do with GraphQL and to write and test your GraphQL requests without leaving your browser. Using this IDE, we will continue to explore examples of GraphQL queries and mutations. We’ll look at the fundamental parts of a GraphQL request and test examples from the official GitHub GraphQL API.

1. The GraphiQL editor

When thinking about the requests your client applications need to make to servers, you can benefit from a graphical tool to help you first come up with these requests and then test them before committing to them in application code. Such a tool can also help you improve these requests, validate your improvements, and debug any requests that are running into problems. In the GraphQL world, this tool is called GraphiQL (with an i before the QL and pronounced “graphical”). GraphiQL is an open source web application written entirely with React.js and GraphQL, and it can be run in a browser.

GraphiQL is one of the reasons GraphQL is popular. It is easy to learn, and it will be a very helpful tool for you. I guarantee that you will love it. It is one of my favorite tools for frontend development, and I cannot imagine working on a GraphQL-based project without it.

You can download GraphiQL and run it locally, but an easier way to get a feel for what this tool has to offer is to use it with an existing GraphQL API service like the Star Wars service that we previewed in chapter 1.

Head over to az.dev/swapi-graphql in your browser to find the GraphiQL editor, which works with the Star Wars data and is publicly available for you to test. Figure 2.1 shows what it looks like.

ch02 fig 01 gqlia
Figure 2. 1. The GraphiQL editor

This editor is a simple two-pane application: the left pane is the editor, and the right pane is where the results of executing GraphQL requests appear.

Go ahead and type the following simple GraphQL query in the editor.

Listing 2. 1. A query for the person field
{
  person(personID: 4) {
    name
    birthYear
  }
}

This simple GraphQL query asks for the name and birth year of the person whose ID is 4. To execute the query, you can press Ctrl-Enter or press the run button (with the little black triangle). When you do, the result pane shows the data that the query is asking for, as shown in figure 2.2.

ch02 fig 02 gqlia
Figure 2. 2. Executing queries with GraphiQL

The best thing about the GraphiQL editor is that it provides intelligent type-ahead and auto-completion features that are aware of the GraphQL type schema you are currently exploring. For the previous example, the editor is completely aware that there is a person object with name and birthYear fields. In addition, the editor has live syntax and validation error highlighting for any text you type.

The awesome features in GraphiQL are all possible because of the documentation GraphQL schema. With one big query to the server, this editor can know about everything the server offers.

To explore these features, clear the editor pane (you can select all the text in the editor with Ctrl-A). Then, just type an empty set of curly brackets: {}. Place your cursor within this empty set, and press Ctrl-Space. You get an auto-completion list like that shown in figure 2.3.

ch02 fig 03 gqlia
Figure 2. 3. GraphiQL’s type-ahead list

Nice! You can very quickly start exploring what fields this GraphQL API offers right there in the editor while you are thinking about your requests. The person field we used before is one of the items in the list.

This list will also be used to auto-complete fields as you type them. Type p, and notice how the list changes to highlight what starts with p. Then, type an e and see how the list only highlights the person field. Press Enter to "pick" the currently highlighted item in the list.

The great thing about this type-ahead list is its context-awareness. It shows you the fields available on the level where you are typing. For example, now that you picked the person field, type another empty set of curly brackets after the word person, put your cursor within this new set, and bring up the type-ahead list by pressing Ctrl-Space. You should see a new list, this time with all the fields you can ask for in the context of a person object (figure 2.4).

ch02 fig 04 gqlia
Figure 2. 4. GraphiQL’s type-ahead list is context-aware.

Context-awareness is extremely helpful, and I am talking not about the “less typing” aspect but rather about the discoverability and validation aspects that enable you to work more quickly and make fewer mistakes. This is an example of the power and control I was talking about in the previous chapter. And that is how GraphQL is different.

Before we pick the name and birthYear fields again, note that one of the closing curly brackets has a red underline. This is part of the live error highlighting you also get in this tool. Discard the type-ahead list by pressing Esc, and hover your mouse cursor over the underlined curly bracket. You should see an error complaining about unexpected syntax. This is because the text in the editor is not yet valid GraphQL syntax. Every time you start a new level of curly brackets, known as a selection set, it needs its own fields.

Go ahead and pick the name and birthYear fields within the person field. The query syntax is now valid (the red underline is gone), but the query is still missing one important piece — and this time, it is not a syntax problem.

You can always execute the query to see what the server has to say about it. If the server rejects the query, it will most likely give you a good reason why it did. For example, executing the query we have right now returns the following.

Listing 2. 2. Example GraphQL error response
{
  "errors": [
    {
      "message": "must provide id or personID",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "person"
      ]
    }
  ],
  "data": {
    "person": null
  }
}

Note that the response in listing 2.2 is a normal JSON response (200-OK) and that it gives two top-level properties: an errors property that is an array of error objects and a data property that represents an empty response. A GraphQL server response can represent partial data when that server has errors about other parts of the response. This makes the response more predictable and makes the task of handling errors a bit easier.

The error message here is helpful: the path "person" must provide id or personID. Since we are asking the server about one person, it needs a way to identify which person’s data to return. Note again that this was not a syntax problem but rather a missing-required-value problem.

To make a path provide a value, we use syntax similar to calling functions. Place the cursor right after the word person, and type the ( character. GraphiQL auto-completes it and shows you a new type-ahead list that, this time, knows what values can be provided as arguments for the person field (figure 2.5).

ch02 fig 05 gqlia
Figure 2. 5. Exploring field arguments with the type-ahead list

Now you can pick the personID argument, give it a value of 4, and get back to the same query you started with. But this time, you discovered the elements you needed through the powerful features of the GraphiQL editor.

In addition to discovering the structure and types of elements inline while you type them, you can browse the Docs section to see full lists and more details. Click the Docs link in the top-right corner of the editor. You should see a search box that you can use to find any type in the current GraphQL schema. I typed the word person and picked the first result; figure 2.6 shows the schema type Person with its description and fields.

ch02 fig 06 gqlia
Figure 2. 6. GraphiQL shows the documentation schema.

Take a moment to explore more of what the GraphiQL editor has to offer. Try more queries, and get a feeling for how easy it is to come up with them.

2. The basics of the GraphQL language

To ask any GraphQL server for data, you send it a request written in the GraphQL query language. A GraphQL request is a tree of fields. Let’s explore these two fundamental concepts of the language.

2.1. Requests

At the core of a GraphQL communication is a request object. The source text of a GraphQL request is often referred to as a document. A document contains text that represents a request through operations like queries, mutations, and subscriptions. In addition to the main operations, a GraphQL document text can contain fragments that can be used to compose other operations, as we will see in the next chapter.

A GraphQL request can also contain an object representing values of variables that may be used in the request document text. The request may also include meta-information about operations (figure 2.7). For example, if the request document contains more than one operation, a GraphQL request must include information about which operation to execute. If the request document contains only one operation, the GraphQL server will just execute that. You do not even need to label the operation with a name in that case, but naming operations is a good practice to follow.

ch02 fig 07 gqlia
Figure 2. 7. The structure of a GraphQL request

Let’s look at a full GraphQL request. Here is a hypothetical example (don’t worry about the new syntax just yet).

Listing 2. 3. Example GraphQL request: document
query GetEmployees($active: Boolean!) {
  allEmployees(active: $active) {
    ...employeeInfo
  }
}

query FindEmployee {
  employee(id: $employeeId) {
    ...employeeInfo
  }
}

fragment employeeInfo on Employee {
  name
  email
  startDate
}

Since this document uses generic variables (the ones starting with the $ sign), we need a JSON object to represent values specific to a request.

Listing 2. 4. Example GraphQL request: variables
{
  "active": true,
  "employeeId": 42
}

Also, since the document contains more than one operation (GetEmployees and FindEmployee), the request needs to provide the desired operation to be executed.

Listing 2. 5. Example GraphQL request: meta-information
operationName="GetEmployees"

If we send all three elements of this request to a GraphQL server, it will parse the whole document, pick the GetEmployees query, fill the variable values, and return the query result.

Three types of operations can be used in GraphQL:

  • Query operations that represent a read-only fetch

  • Mutation operations that represent a write followed by a fetch

  • Subscription operations that represent a request for real-time data updates

We will see practical examples of these operations in the next section.

The example in listing 2.3 represented a query operation. Here is a hypothetical example of a mutation operation.

Listing 2. 6. Example GraphQL mutation operation
mutation {
  addRating(storyId: 123, rating: 5) {
    story {
      averageRating
    }
  }
}

The mutation operation in listing 2.6 adds a new five-star rating record for a story and then retrieves the new average rating of that same story. Note that this is a write followed by a read. All GraphQL mutation operations follow this concept.

The mutation operation in listing 2.6 does not have a name. An anonymous operation is okay if it is the only operation defined in the document. If the anonymous operation is a query, we can also omit the word query. This is what we did when we started exploring the GraphiQL editor.

Here is a hypothetical example of a subscription operation.

Listing 2. 7. Example GraphQL subscription operation
subscription StoriesRating {
  allStories {
    id
    averageRating
  }
}

The subscription operation in listing 2.7 instructs the GraphQL server to open a socket connection with the client, send story IDs along with their average ratings, and keep doing that when the information changes on the server. This feature is a much better alternative than continuously polling data to keep a UI view up to date.

2.2. Fields

One of the core elements in the text of a GraphQL request is the field. The simplest way to think about a GraphQL request is as a way to select fields on objects.

A field always appears within a selection set (inside a pair of curly brackets). A selection set is primarily composed of fields.

A field in GraphQL describes one discrete piece of information that you can retrieve about an object. It can describe a scalar value (like the name of a person or their birth year), an object (like the home planet of a Star Wars character), or a list of objects (like the list of films in which a Star Wars character appeared). For the last two cases, the fields contain another selection set to customize the information needed about the objects the fields describe.

Here is an example GraphQL query with different types of fields.

Listing 2. 8. GraphQL fields
{
  me {
    email
    birthday {
      month
      year
    }
    friends {
      name
    }
  }
}

The fields email, month, year, and name are all scalar fields. Scalar types represent primitive leaf values. GraphQL schemas often support four major scalar types: Int, String, Float, and Boolean. The built-in custom scalar value ID can also be used to represent identity values. We’ll see an example in chapter 4.

The term leaf comes from Graph theory. It means a vertex with no children.

You can also customize a GraphQL schema to support more scalar values with certain formats. For example, a schema can be designed to have a Time scalar value representing a time value in a standard and parsable format (ISO/UTC).

The me and birthday fields describe objects, so they require their own nested selection sets to represent their properties. The friends field describes a list of friend objects, so it also requires a nested selection set to represent the properties of the objects in that list.

All GraphQL operations must specify their selections down to fields that return scalar values (leaf values). For example, they cannot have fields that describe objects without providing further nested selection sets to specify which scalar values to fetch for these objects. The last nested level of fields should always consist of only fields that describe scalar values. For the example in listing 2.8, if you did not specify the nested selection set for the friends field (the { name } part), the GraphQL query would not be valid because in that case, not all of the last nested level fields would describe scalar values.

The root fields in an operation usually represent information that is globally accessible to your application and its current user.

I use the term root field to refer to the first-level fields in a GraphQL operation.

Some typical examples of root fields include references to a currently logged‐in user. These are often named viewer or me. For example:

{
  me {
    username
    fullName
  }
}

Root fields are also generally used to access certain types of data referenced by a unique identifier. For example:

# Ask for the user whose ID equal to 42
{
  user(id: 42) {
    fullName
  }
}

In this query, the user field represents one of many users in a graph of data. To instruct the server to pick one user, we specify a unique ID value for the user field.

Note that in the previous example, the # character is used to write a comment about the query. This is the official character to comment a single line (or the remainder of a line) in a GraphQL document. There is no supported way to have multiline comments in GraphQL documents, but you can have many lines, each of which starts with the # character. The server will just ignore all the comments. It will also ignore any extra spaces, all line terminators, and all insignificant commas between fields. These characters can be used to improve the legibility of source text and emphasize the separation of tokens. They have no significance to the semantic meaning of GraphQL documents.

3. Examples from the GitHub API

Now that you know about requests, documents, queries, and fields, let’s put this knowledge to use and explore some real-world examples of GraphQL requests from the GitHub API. GitHub moved from REST APIs to GraphQL APIs in 2017. We can use their GraphQL API explorer at az.dev/github-api; this embedded GraphiQL editor (figure 2.8) includes the proper authentication headers for the API (you need to be logged in with a GitHub.com account).

You can also use a standalone GraphiQL editor to explore the GitHub API (see az.dev/graphiql-app). You have to manually include an access token in that app. You can use this standalone app with any GraphQL API service.
ch02 fig 08 gqlia
Figure 2. 8. The GraphiQL editor for the GitHub API

Let’s first look at some common queries from this API.

The GitHub API uses your real, live production data at GitHub.com.

3.1. Reading data from GitHub

When you first launch the GitHub GraphQL API explorer, it has a default simple query that displays your login. The currently logged-in user is represented by the viewer field. Under this field, you can read all the information that is available about you at GitHub.

For example, here is a query to see information about the most recent 10 repositories that you own or contribute to.

Listing 2. 9. Your most recent repos (az.dev/gia)
{
  viewer {
    repositories(last: 10) {
      nodes {
        name
        description
      }
    }
  }
}

Here is another query to see all the supported licenses in GitHub along with their URLs.

Listing 2. 10. All GitHub-supported licenses (az.dev/gia)
{
  licenses {
    name
    url
  }
}

Next, here is a more complex query to find the first 10 issues of the facebook/graphql repository. It asks for the name of the author and the title used for the issue page, along with the date when the issue was created.

Listing 2. 11. The first 10 issues of a repo (az.dev/gia)
{
  repository(owner: "facebook", name: "graphql") {
    issues(first: 10) {
      nodes {
        title
        createdAt
        author {
          login
        }
      }
    }
  }
}

3.2. Updating data at GitHub

Let’s now explore some mutations we can do with the GitHub GraphQL API. The simplest mutation is to "star" a repository. If you execute the following mutation under your logged-in user, its action is equivalent to going to github.com/jscomplete/graphql-in-action and clicking the star button.

Listing 2. 12. "Star" a repository (az.dev/gia)
mutation {
  addStar(input: { starrableId: "MDEwOlJlcG9zaXRvcnkxMjU2ODEwMDY=" }) {    (1)
    starrable {
      stargazers {
        totalCount
      }
    }
  }
}
1 Use listing 2.13 to find this starrableId value

The mutation stars the repository and then reads the new total number of stargazers after the mutation. The input for this mutation is a simple object that has a starrableId value, which is the node identifier for the graphql repository. I was able to find that value using this query.

Listing 2. 13. Find a repo ID (az.dev/gia)
{
  repository(name: "graphql-in-action", owner: "jscomplete") {
    id
  }
}

Let’s execute another mutation. This time, let’s add a comment to an issue in a repository. I created an issue for you to test this mutation under the repository at github.com/jscomplete/graphql-in-action. You can see the details of this issue using the following query.

Listing 2. 14. The details of one issue under a repo (az.dev/gia)
query GetIssueInfo {
  repository(owner: "jscomplete", name: "graphql-in-action") {
    issue(number: 1) {
      id
      title
    }
  }
}

This gives you the value of the id field needed to add a comment to the issue using a mutation. Now execute the following mutation, which uses the id value you found using the query in listing 2.14.

Listing 2. 15. Add a comment to a repo issue (az.dev/gia)
mutation AddCommentToIssue {
  addComment(input: {
    subjectId: "MDU6SXNzdWUzMDYyMDMwNzk=",
    body: "Hello from California!"  (1)
  }) {
    commentEdge {
      node {
        createdAt
      }
    }
  }
}
1 Tell us where you’re from in your test comment. :)

After the mutation in listing 2.14 saves your comment to the special issue, it reports the createdAt date for that comment. Feel free to send as many comments as you wish to this special issue, but only do so through the GraphQL API explorer.

You can see the comments you added and all the other comments on this issue at github.com/jscomplete/graphql-in-action/issues/1.

3.3. Introspective queries

GraphQL APIs support introspective queries that can be used to answer questions about the API schema. This introspection support gives GraphQL tools powerful functionality, and it drives the features we have been using in the GraphiQL editor. For example, the awesome type-ahead list in GraphiQL is sourced with an introspective query.

Introspective queries start with a root field that’s either __type or __schema, known as meta-fields. There is also another meta-field, __typename, which can be used to retrieve the name of any object type. Fields with names that begin with double underscore characters are reserved for introspection support.

Meta-fields are implicit. They do not appear in the fields list of their types.

The __schema field can be used to read information about the API schema, such as what types and directives it supports. We will explore directives in the next chapter.

Let’s ask the GraphQL API schema what types it supports. Here is an introspective query to do that.

Listing 2. 16. Example GraphQL introspective query (az.dev/gia)
{
  __schema {
    types {
      name
      description
    }
  }
}

This query returns all the types this schema supports, and it also includes the descriptions of these types. This is a helpful list to explore the custom types defined in the GitHub GraphQL schema. For example, you should see that the GitHub API schema defines types like Repository, Commit, Project, Issue, PullRequest, and many more (figure 2.9).

ch02 fig 09 gqlia
Figure 2. 9. Listing all the supported types under the GitHub API schema

If you need to retrieve information about a single type, you can use the __type meta-field. For example, here is a query to find all the supported fields under the type Commit along with any arguments they accept.

Listing 2. 17. Supported fields under a Commit object (az.dev/gia)
{
  __type(name: "Commit") {
    fields {
      name
      args {
        name
      }
    }
  }
}

Use the GraphiQL type-ahead feature to discover what other information you can ask for under these introspective meta-fields.

4. Summary

  • GraphiQL is an in-browser IDE for writing and testing GraphQL requests. It offers many great features to write, validate, and inspect GraphQL queries and mutations. These features are made possible thanks to GraphQL’s introspective nature, which comes with its mandatory schemas.

  • A GraphQL request consists of a set of operations, an object for variables, and other meta-information elements as needed.

  • GraphQL operations use a tree of fields. A field represents a unit of information. The GraphQL language is largely about selecting fields on objects.

  • GitHub has a powerful GraphQL API that you can use to read data about repositories and users and do simple mutations like adding a star to a repository or commenting on an issue in a repository.

  • GraphQL introspective queries offer a way for clients to get meta-information about the GraphQL API.