Graphql In Action
Exploring Graphql Apis

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 API

  • Exploring GraphQL’s introspective features

  • Exploring auto-generated GraphQL APIs

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 leverages GraphQL’s type system to give you features that 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’ll continue to explore examples of GraphQL queries and mutations. We’ll look at the fundamental parts of a GraphQL request, we’ll test examples from the official GitHub GraphQL API, and we’ll also test a GraphQL backend-as-a-service tool.

1. The GraphiQL Editor

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

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

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

Head over to graphql.org/swapi-graphql in your browser. The page that loads up under that URL has the GraphiQL editor, which works with the Star Wars data and is publicly available for you to test. Here is what it looks like:

ch02 fig 01 gqlia
Figure 2. 1. The GraphiQL editor at graphql.org/swapi-graphql

This editor is a simple 2-pane application where the left pane is the editor and the right pane is where the result of executing your GraphQL requests will appear.

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

Listing 2. 1. Query for person | az.dev/gia
{
  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 will show the data that the query is asking for:

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

The best thing about this 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, this means that the editor is completely aware that there is a person object that has 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 schema which is mandatory for a GraphQL server to publish.

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

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

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

This list will also be used to auto-complete fields as you type them. Type “p” and notice how the list is now changing to highlight what starts with “p”. Then, continue typing an “e” and see how the list will only highlight the person field. Hit Enter to "pick" the currently highlighted item in the list.

The great thing about this type-ahead list is its accurate context-awareness. It showed you the top-level fields when you were typing at the top-level area and it will show you a different set of fields if you are within another field. For example, now that we picked the person field, go ahead and type another empty set of curly brackets after the word "person" and put your cursor within this new set and bring up the type-ahead list with Ctrl+Space. You should see a new list now and this time the list will have all the fields that you can ask for within the context of a person object!

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

This is extremely helpful, and I am not talking about the less typing aspect of it but rather the discoverability and validation aspects which enable you to be faster and make less mistakes. This is an example of the power and control I was talking about in the previous Chapter. This is how GraphQL is different.

Before we pick the fields name and birthYear again, note how 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 that underlined curly bracket. You should see an error complaining about some unexpected syntax. This is because the text we have in the editor so far is not valid GraphQL syntax yet. Every time you start a new level of curly brackets, which by the way is named a selection set in GraphQL, that selection set needs to have fields of its own.

Go ahead and pick the fields name and birthYear 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 will return 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 how this error response was a normal JSON response (a 200-OK one) and how it is giving us back two top-level properties: one 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 was a helpful one. 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 will auto-complete that with ")" and show you a new type-ahead list that, this time, is aware of what values can be provided as arguments for the person field.

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 that we started with, but this time, you discovered the elements that you needed through the powerful features of the GraphiQL editor.

Besides discovering the structure and types of these elements inline while you type them, you can browse the "Docs" section to see full lists and more details about them. 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 within the current GraphQL schema. I typed the word "person" and picked the first result. This is showing 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 this GraphiQL editor has to offer. Try more queries and get a feeling of 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. It might also contain fragments that are defined and used to compose other operations.

A GraphQL request might also contain an object to represent values of variables that might be used in the request document. The request might also include meta information about the operations. 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 a hypothetical example of that (don’t worry about the new syntax you’ll see here 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. For example:

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. For example:

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 variables values, and return the result of that query.

There are three types of operations that 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 operation in the next section of this chapter.

The example in listing 2.2 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.5 will add a new 5-star rating record for a story and it will then retrieve the new average rating of that same story. Note how this is a write followed by a read. All GraphQL mutation operations follow this concept.

The mutation operation in listing 2.5 did 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.6 will instruct the GraphQL server to open a socket connection with the client, send stories’ IDs along with their average ratings, and keep doing that when that information changes on the server.

2.2. Fields

One of the core elements in the text of a GraphQL request is the field. The most simplified way to think about a GraphQL request is to think about it 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 could be describing a scalar value (like the name of a person or their birth year), or it could describe an object (like the home planet of a Star Wars character), or it could describe a list of objects (like the list of films in which a Star Wars character appeared). For the last two cases, their fields will contain another selection set to customize the information needed about the objects that are described by them.

Here is an example GraphQL query that has examples of 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. The fields me and birthday are fields that describe objects, and therefore they needed their own nested selection sets. The field friends describes a list of friend objects and therefore it also needed a nested selection set to represent each object in that list.

All GraphQL operations must specify their selections down to fields which return scalar values. They cannot, for example, 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.7, if you do not specify the nested selection set for the field friends (the { name } part), the GraphQL query would not have been valid because not all of the last nested level fields would be describing scalar values in that case.

The top-level fields in an operation usually represent some information that is globally accessible to your application and its current viewer. Some typical examples of these top fields include references to a current logged‐in viewer or fields accessing certain types of data referenced by a unique identifier.

{
  me {
    fullName
  }
}

In this query, the field “me” usually represent the currently logged-in user. Other APIs name this top-level field “viewer” instead.

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

In this query, the field user 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 how in the previous example the # character was used to write a comment about the query. This is the official character to comment a single line (or remainder of a line) in a GraphQL document. There is no supported way to have multi-line comments in GraphQL documents, but you can just have many lines that each start with the # character. The server will just ignore all the comments. It will also ignore any extra spaces, all the line terminators, and all insignificant commas between the fields. All 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 we know about requests, documents, queries, and fields, let’s put this knowledge to use and explore some real-world examples of GraphQL requests that you can ask the GitHub API for. GitHub moved from REST APIs to GraphQL APIs in 2017. You can explore the new GitHub API at https://developer.github.com. You need to be logged in (with a GitHub.com account) to use this API and it is subject to rate limiting.

This GitHub API makes use of your real, live, production data at GitHub.com

Under the "API Docs" menu, you should see both the GraphQL API and the REST API. Navigate to the GraphQL API and then to the Explorer tab. Once you are logged into github.com, you should see an embedded GraphiQL editor that will enable you to explore the GitHub GraphQL API.

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.

3.1. Reading Data From GitHub

When you first launch the GitHub GraphQL API explorer, it has a default simple query that will display your own login. The currently logged-in user is represented by the field “viewer”. 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 last 10 repositories that you own or contribute to:

Listing 2. 9. Query for the last 10 repositories for logged-in user | 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. Query for all GitHub-supported licenses | az.dev/gia
{
  licenses {
    name
    url
  }
}

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 that was used for the issue page along with the date that issue was created:

Listing 2. 11. Query for the first 10 issues of a repository | 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 repo. Here is a mutation which, if you execute under your logged-in user, its action would be equivalent to you going to github.com/facebook/graphql and clicking the "star" button:

Listing 2. 12. Mutation to "star" a repository | az.dev/gia
mutation {
  addStar(input: {starrableId: "MDEwOlJlcG9zaXRvcnkzODM0MjIyMQ=="}) {    (1)
    starrable {
      stargazers {
        totalCount
      }
    }
  }
}
1 Use listing 2.12 to find this starrableId value

The mutation will star the repo and then read 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. Query to find an id of a repository | az.dev/gia
{
  repository(name: "graphql", owner: "facebook") {
    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 that I am about to comment on for the first time using this query:

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

This will give you the value of the id field that is needed to add a comment to the issue using a mutation. Now you need to execute the following mutation that uses the id value you found using the query in listing 2.13.

Listing 2. 15. Mutation to add a comment to a repository issue | az.dev/gia
mutation AddCommentToIssue {
  addComment(input: {
    subjectId: "MDU6SXNzdWUzMDYyMDMwNzk=",
    body: "Hello GraphQL"
  }) {
    commentEdge {
      node {
        createdAt
      }
    }
  }
}

After the mutation in listing 2.14 saves your comment to the special issue, it will report 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 https://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 itself. This introspection support enables GraphQL tools to have powerful features and it is what 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 top-level field that’s either __type or __schema, which are known as meta-fields. There is also another meta-field named __typename that 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, which means they do not appear in the fields list of their types.

The __schema field can be used to read information about the API schema, like 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 for that:

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

This query will return all the types this schema supports and it will also include each type’s description. This is a helpful list to explore the custom types defined in the GitHub GraphQL schema. For example, you should see that this schema defines types like Repository, Commit, Project, Issue, PullRequest, and many more.

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. Query for 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. Using Backend Services

With the rise of GraphQL’s popularity, many businesses and startups started offering GraphQL-related services and tools. You can use most of these services by directly hosting them yourself, or you can use them online through their providers with various subscription models.

One example of these services is what’s known as "Backend as a Service". An application from these services will usually allow you to define your data models using a web user interface and it will then instantly give you read and write access to a GraphQL API that the service generates for all the data models you define.

An example of an excellent backend-as-a-service tool is Graphcool (available at https://graph.cool). You can host the open-source Graphcool framework yourself or use it online. It offers an intuitive UI and a solid runtime and the project is an actively-maintained, open-source, and community-driven effort.

Another option for a GraphQL backend-as-a-service application is one that I am personally involved in. It is called GraphFront and it is an educational, free, and open-source project that is meant to offer the simplest possible UI and runtime for a GraphQL backend-as-a-service tool. It works without the need to configure anything and the UI is intentionally minimalistic to provide simple features. We will be exploring parts of the source code for the GraphFront project later in this book, but now we will test the online service and see how easy it is to get up and running on a GraphQL API with backend-as-a-service tools like Graphcool and GraphFront.

Head over to graphfront.com and create an account there. Once you are logged in to your new account, you should see a screen that looks like this:

ch02 fig 10 gqlia
Figure 2. 10. The GraphFront.com main UI

Notice how in the left sidebar of Figure 2.10, GraphFront created an API Endpoint for you and assigned that endpoint an API Key. You do not have any data models yet, so the API endpoint will not have any capabilities. Once you start adding models, the API endpoint will automatically be supporting them.

Let’s define a "Note" model designed to record general text notes and let’s give it the following three fields: a number, a body, and an isMarkDown flag. Click on the "New Model" button and use the form there to create the "Note" model with its fields. Here is my screen before I created my "Note" model:

ch02 fig 11 gqlia
Figure 2. 11. Creating a new model at GraphFront

Note how the number field has an Int type, the body field has a String type, and the isMarkdown field has a Boolean type. Note also how only the body field is required.

Once you have a model defined in your dashboard, head over to your assigned API endpoint and you should see an instance of GraphiQL running over there. You can use the following mutation to add a test note to your "Note" model.

Listing 2. 18. Mutation to add a test Note record in your GraphFront account | az.dev/gia
mutation AddTestNote {
  createNote(
    apiKey: "YOUR GRAPHFRONT API KEY",
    input: {
      number: 1,
      body: "This is just a test",
      isMarkDown: false,
    }
  ) {
    id
  }
}

Note a few things about the mutation in listing 2.17:

  • The name of the mutation can be anything, I named this one AddTestNote

  • The top-level field used in the mutation and the structure of its input and readable values were all generated by GraphFront based on the model that we just defined

  • All operations for GraphFront require passing the value of your API Key which you can find in your GraphFront dashboard

Once you successfully create this note, GraphiQL will report its database-generated id value. You can also refresh your dashboard to see the test note under the Note model.

ch02 fig 12 gqlia
Figure 2. 12. Listing models' records at GraphFront

Note how GraphFront added three fields to the model. An Id field that acts as the primary identifier of this model’s records, a createdAt field that will have the creation date of each note, and an updatedAt field that will have the last modification date of each note when they get updated.

Go ahead and create a few more test notes by executing the same AddTestMutation operation, but with different values for the title and body input arguments.

Use GraphiQL’s type-ahead and other introspective-based features to discover what other operations you can do on the "Note" model. You should be able to Create, Read, Update, and Delete records (CRUD). Here is a query you can use to read all the notes that you have created so far:

Listing 2. 19. Query for all the note records from your GraphFront account | az.dev/gia
query GetAllNotes {
  viewer(apiKey: "YOUR GRAPHFRONT API KEY") {
    notes {
      id
      number
      body
      isMarkDown
      createdAt
      updatedAt
    }
  }
}

GraphFront and other backend-as-a-service tools act like your cloud-based database, and they come with a GraphQL API for you to read and modify that data, which is simply a great time-saver to write small projects and to prototype things quickly without needing to host or manage a database. However, these services will force you to do things a certain way and they will always be limited and might not satisfy all your data storage and communication needs. You need to learn how to build your own GraphQL APIs and work with your own storage engines. We are almost ready for that, but first let me tell you a bit more about the GraphQL query language and the many features it offers to customize and organize your requests.

5. 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 introspective nature that comes with its mandatory schemas.

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

  • GraphQL operations use a tree of fields. A field represent 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, as well as do simple mutations like add a star to a repository, or comment on an issue in a repository.

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

  • You can use GraphQL backend-as-a-service tools to create your own data models and have ready-to-use GraphQL requests to read and write to these data models. This is an easy way to get started with a GraphQL API and quickly prototype designs.

In the next chapter, we will learn how to customize and organize GraphQL requests.