GraphQL is often touted for its efficiency when consuming data from APIs. There are two key aspects to its efficiency:
- You get only the data you need: GraphQL will only respond with the fields you specify. This means it doesn't need to send unnecessary data across the wire, potentially saving your application – and your users – significant amounts of data over the course of numerous transactions.
- You get all the data you need in a single request: Rather than spread different parts of the data across multiple endpoints, GraphQL lets you get all the data you require via a single request. For example, you can get user information and their order information in a single query, rather than sending requests to two separate endpoints.
Both of these benefits are made possible by the way GraphQL lets you construct a query. Let's look at some of the basics of constructing a query in GraphQL to retrieve data:
- Get Set up to Create Queries
- Create your First Query
- Types of Queries:
Get Set up to Create Queries
The GitHub GraphQL API is a large and complex GraphQL API that offers a way to explore many of GraphQL's query features.
There are two ways to explore the API:
-
API Explorer: Use GitHub's API Explorer. When you log in to your GitHub account, the API Explorer enables you to explore the API schema and construct and run queries via a GraphiQL interface.
Note: To use the API outside of the API Explorer, you must generate a Personal Access Token.
-
External Desktop Apps: Use a desktop app like:
- Postman: A collaboration platform for API development that supports GraphQL.
- GraphQL Playground: An Electron desktop app that can be installed via Homebrew or by downloading the latest release.
This tutorial uses GraphQL Playground.
Follow the steps below set up and use the API:
-
Add the API endpoint:
https://api.github.com/graphql
for GraphQL Playground. At this point you'll get a 401 error if you try to run anything. You'll fix that in the next step. -
Add an
Authorization
header to the HTTP headers (located at the bottom left of the application window):{ "Authorization":"Bearer MY_TOKEN" }
-
Click the Docs tab on the far right to explore the types and queries available within the GitHub API.
Now you're ready to start building a query.
Create your First Query
Start with a relatively simple topic
query, to find out how many people are following a "GraphQL" topic on GitHub.
With a RESTful API, this might involve calling an endpoint such as /v3/search/topics
that returns all of the necessary details. However, every GraphQL query calls the same endpoint, so you must define what you are querying in the request. Luckily, GraphQL IDEs like GraphiQL or the GraphQL Playground offer code hinting.
Start by calling the topic
query:
{ topic }
You may notice that your GraphQL IDE indicates that there is an error in your request.
The error indicates the topic
query expects a name
argument of type string. So, add your first argument in parentheses after the query:
{ topic(name: "graphql") }
Even with this change, an error still occurs. Why? GraphQL requires you to specify the fields required within your query, in order to supply you with only the data you need. There is no equivalent of select *
in GraphQL, and this is for a number of good reasons. So, let's specify some fields within the query.
The topic
query has six fields available, but you're only interested in the number of people following it, or the stargazersCount
field, so modify the query to look as follows:
{ topic(name: "graphql") { stargazerCount } }
Finally, no errors. Run our query. The response should look similar to the following:
{ "data": { "topic": { "stargazerCount": 12201 } } }
Notice that the response matches the query, meaning you don't need to waste time figuring out the structure of the response.
Arguments
The above example uses a single string argument. In addition to any of the GraphQL basic types, an argument can also be an enum or even a custom object type. For example, GitHub's search
query takes a searchType
enum argument that can be set to ISSUE
,REPOSITORY
or USER
:
{ search(query: "GraphQL", type: REPOSITORY) { repositoryCount } }
The example below shows a custom object called repositories
within a user
query. It takes a custom object argument of type RespositoryOrder
that specifies both the field to order by and the direction in which to order:
... repositories(orderBy: {field:UPDATED_AT, direction: DESC}) { ...
Note: This snippet is from a larger query that will be explored in more detail further down.
The point here is that GraphQL enables APIs to support very complex querying capabilities in arguments.
Nested Objects
So far, you have a very simple query that gets a single field. Let's make this a little bit more complex by adding a nested object to get multiple sets of data in a single query. In this case, you will use the relatedTopics
object to get a list of topics related to "GraphQL" within GitHub:
{ topic(name:"graphql") { stargazerCount relatedTopics { name stargazerCount } } }
You must specify the object and the fields within that object that you want to retrieve. In this case, relatedTopics
did not require an argument. However, if you look at the docs, you'll see that it does have an optional argument (first
) that defaults to 3
, meaning it will only send the first three results. Let's define that to return 10
(the maximum the API allows):
{ topic(name:"graphql") { stargazerCount relatedTopics(first: 10) { name stargazerCount } } }
This shows that you can specify arguments both on the primary query and on nested object queries, as defined by the schema.
Aliases
Now imagine that you want to get the stargazer count for both GraphQL and JavaScript in a single request. You might try something like this:
{ topic(name: "graphql") { stargazerCount } topic(name: "javascript") { stargazerCount } }
That results in an error because you defined conflicting queries in a single request. To solve this, use an alias on each of the queries:
{ graphql:topic(name: "graphql") { stargazerCount } javascript:topic(name: "javascript") { stargazerCount } }
This returns a response containing the aliases you specified:
{ "data": { "graphql": { "stargazerCount": 12203 }, "javascript": { "stargazerCount": 73700 } } }
It's worth noting that the alias can be any string you want, though it cannot start with a number or contain any special characters other than an underscore (though it can start with an underscore).
Fragments
Let's modify this query to get more than just the stargazerCount
. To avoid writing code for each aliased query, you can use fragments which represent reusable pieces of query code. For example, instead of writing this:
{ graphql:topic(name:"graphql") { stargazerCount relatedTopics(first: 10) { name stargazerCount } } javascript:topic(name:"javascript") { stargazerCount relatedTopics(first: 10) { name stargazerCount } } }
You can encapsulate the reusable code in a fragment
like this:
{ graphql:topic(name:"graphql") { ...topicDetails } javascript:topic(name:"javascript") { ...topicDetails } } fragment topicDetails on Topic { stargazerCount relatedTopics(first: 10) { name stargazerCount } }
You define the fragment by specifying fragment
followed by a name (topicDetails
in this case) and its type (Topic
in this case) for validation purposes.
Then, you can reuse the fragment by referencing its name, prefixed by three dots (...topicDetails
in this case) wherever you need to reuse it. While this example doesn't save a lot of code, it can be particularly useful when you find yourself repeating large chunks of code in a complex query.
Variables
Imagine you want to create a query that gets the user profile information and recent repositories for a user. This wouldn't work if you had to pass a hard-coded user string. Instead, GraphQL enables you to define a variable and then pass in a variables object to the query to populate the variables.
First, let's look at the query. This query basically gives you everything you might want to know to construct a profile page for a GitHub user (perhaps for a developer portfolio home page or something):
query GitHubUserProfile($username: String!) { user(login:$username) { name company bio twitterUsername websiteUrl url repositories(first: 10, privacy: PUBLIC, orderBy: {field:UPDATED_AT, direction: DESC}) { totalCount edges { node { name description url } } } } }
Up until now, you have been using an anonymous operation (i.e., wrapping the query in curly braces). Best practice says you should define the operation type and name. In this case, the operation type is a query
and the name is GitHubUserProfile
. There are other operation types such as a mutation (modifies data on the server) and a subscription (subscribes to updates to changes from the server).
You defined a variable $username
(all variables must be prefixed by a $
) of type String
. The exclamation point (!
) indicates that this variable is a required argument. Within the user
query, you now reference the $username
variable as the value for the login
argument.
To specify that argument, you must pass in a JSON object containing a value for username
. You can do that in GraphQL Playground via the Variables tab at the bottom left of the application:
Supply a GitHub username:
{ "username": "remotesynth" }
You can see the result of running the query on the right.
For More Information
Visit the following resources for more information: