Sending First GraphQL Queries
Learn the basics of GraphQL query language and use it to interact with the GitHub API.
Let’s learn how to manually send some simple queries to an existing public GraphQL API. Once we’re comfortable with using GraphQL, we’ll start working on automating the queries with the help of the application.
GitHub GraphQL API
There is no shortage of public GraphQL APIs, but we’ll use GitHub’s API as our training ground in this lesson. There are a few reasons for this choice. First, its API is easy to access because it’s available to everyone with a GitHub account. Second, GitHub’s API is considered one of the best and can be a good source for learning best practices when designing an API. And last but not least, almost everyone is familiar with what GitHub is.
GitHub provides a handy UI tool for exploring and interacting with the API, which is available here.
This tool allows us to send GraphQL queries to GitHub, inspect responses, and explore the API documentation.
As we proceed through each lesson, it can be helpful to go to the GitHub page and run the queries to see how they work. They don’t even need to be modified. Before getting started with the API, we suggest logging into your GitHub account. To do this, click the green button at the top right of the Explorer screen.
The interface of this GraphQL explorer has three parts:
- A query editor on the left.
- A documentation explorer on the right that provides API documentation.
- A middle section that will contain the results of the last sent query.
We’ll discuss query variables in one of our later lessons, so we can ignore it for now.
To run a query, we’ll enter the text of the query in the left section and then click the “Play” button at the top.
Getting info about the current user
Let’s start with a simple GraphQL query and break it down step-by-step. For our first query, let’s get some basic information about a user currently logged in to GitHub.
We can do it with the following query:
query {viewer {loginisHireablenamelocation}}
Before we run it, let’s learn how it’s structured. The outer layer of this code snippet contains the word query
and curly brackets.:
query {...}
This first keyword specifies which operation we want to perform with the GraphQL API. Using query
means we want to fetch data using the API, but we also have two other options:
-
mutation
: This changes the data on the server. -
subscription
: This retrieves a stream of real-time updates from a GraphQL server.
We’ll look into these other operations later, so let’s focus on queries for now.
The following layer specifies the name of the query we want a GitHub server to execute. In this case, we want to execute a query named viewer
.
query {viewer {...}}
As we briefly discussed in the previous lesson, each GraphQL API provides many queries that work as entry points in a graph of objects. The viewer
query returns information about the currently logged-in user.
The last piece of information we need to provide is a list of fields we want to get from the object returned by the viewer
query.
query {viewer {loginisHireablenamelocation}}
In this case, we want to get login
, isHirable
, name
, and location
. The viewer
query can return a myriad other fields as well as other objects related to a current user, and later we’ll see how we can discover all the available fields.
If we run this query, the GraphQL explorer will send it to a server and display a response.
{"data": {"viewer": {"login": "mushketyk","isHireable": false,"name": "Ivan Mushketyk","location": "Dublin, Ireland"}}}
Notice that the structure of the response matches the query. In the data
object, we have a viewer
field that contains the return value for the viewer
query. It also contains the four fields that we requested from the API.
The result will be the same for any other GraphQL query that we execute. A query defines the shape of the data that a server should return, and all successfully executed queries will return data with the shape defined by a client.
GraphQL schema
Let’s learn how to find a list of queries in the GitHub API and how to get a list of the fields each query can return. This is defined by the GraphQL schema, another core GraphQL concept.
The GraphQL schema is defined for each GraphQL API, specifying what types exist in an API, how these types are related, and what queries and mutations we can use to fetch or update this data. Since the GraphQL schema is such a foundational concept and defines what a GraphQL can do, we’ll learn about it first.
The GraphQL schema is represented using a special language. We can get the whole schema for the GitHub API here, but for now, let’s look at the relevant parts for the viewer
query shown below, with the irrelevant comments and fields removed.
type User {login: String!isHireable: Boolean!name: String!location: String... # Other fields}type Query... # Other queriesviewer: User!}
As we can see, this snippet contains two types of definitions. Type User
shows a list of available fields on a User
object. The GraphQL API is strongly typed, and each field has a type associated with it. We can either define our types or use one of the built-in GraphQL types:
Int
: A signed 32-bit number.Float
: A signed floating-point number.String
: A UTF‐8 string.Boolean
: Either atrue
orfalse
value.ID
: This is the same thing as a string, but specifies that this is a non-human readable ID.
Notice that in most of the fields in the code example above, the name of each type ends with the !
character. This specifies that a value for this field should be non-null and is always defined when returned by a server.
The definition of the location
field is simply the String type, which means it can be null
or a string
.
location: String
Which means that it can be null
or a string.
The second type defined in the schema above defines all queries, including the one we were using in previous examples.
type Query
... # GitHub has many other queries, but we will only focus on this one for now
viewer: User!
}
It specifies that the viewer
query will return an object of a type User
, and the !
character means that it will always be non-null.
Of course, we could download a GitHub schema to learn about what types we can use, but a better way to find relevant type definitions is to use the documentation section of the GraphQL explorer. We can search for either specific queries or types.
Most GraphQL explorers, including the one used by GitHub, also implement auto-completion, which you can activate by using the Ctrl
+Space
shortcut.
Fetching nested objects
As we discussed in the previous lesson, we can also use GraphQL to fetch nested fields. In GitHub API, each user can have a current status that contains a text message and an emoji, and we can get it using the status
field on a User
type.
This is how the status
field is defined in the GraphQL schema:
type User {# Fields that we used beforelogin: String!isHireable: Boolean!name: String!location: String# This is the field that we want to querystatus: UserStatus... # Other fields}## A type refered by the "User" typetype UserStatus implements Node { # We will discuss "implements" keyword later# All fields availablecreatedAt: DateTime!emoji: StringemojiHTML: HTMLexpiresAt: DateTimeid: ID!indicatesLimitedAvailability: Boolean!message: Stringorganization: OrganizationupdatedAt: DateTime!user: User!}
There’s a lot to unpack in this example. First, the schema specifies that we can access a user’s status through the status
field. The status is defined in the UserStatus
type, and it contains some new types that we haven’t discussed yet . The DateTime
and HTML
types are specific types for strings in a particular format, and Organization
is another type with multiple fields that we won’t use in these lessons.
Now let’s try to write a query that returns information about the current user with its status. Since the status
field returns an object with multiple fields, we can’t write a query like this:
query {viewer {loginisHireablenamelocationstatus # this is not allowed}}
Instead, we need to explicitly list all the fields that we want a server to return from the status
field, even if we want all the fields an object has.
To get a status for the current user we could send a query like this:
query {viewer {loginisHireablenamelocationstatus {messageemojiexpiresAt}}}
In this example, GitHub returns this response:
{"data": {"viewer": {"login": "mushketyk","isHireable": false,"name": "Ivan Mushketyk","location": "Dublin, Ireland","status": {"message": "Building a new course","emoji": ":hammer_and_wrench:","expiresAt": "2021-07-11T20:59:59Z"}}}}
Notice that the UserStatus
type has a reference to a user (type User
) that set this status.
query {viewer {status {user {status {user {status {message}}}}}}}
A query like that would return a complex structure like this:
{"data": {"viewer": {"status": {"user": {"status": {"user": {"status": {"message": "Building a new course"}}}}}}}}
This doesn’t get us anything in this particular case, but it shows that we can request arbitrary complex nested structures using GraphQL API.
Client queries
So, how does a client send a GraphQL query?. We can inspect this easily.
First, let’s open a Network tab in our browser’s developer tools, and run a GraphQL request. The result is a single POST request to https://graphql.github.com/graphql/proxy
, simply sent as a string in a JSON object.
Summary
In this lesson, we’ve covered two new GraphQL concepts, sending queries and GraphQL schema. We saw how we can construct simple GraphQL queries and how to fetch nested fields.
Exercises
Let’s try out our newly acquired skills!
Try to send a GraphQL query that gets bio
information about our GitHub user. This is represented as a string in GraphQL, and we can get it using the bio field on the User
type. Notice that it’s defined as a nullable string, so we can get null
if it’s not set for a user.