GraphQL Query Basics

Learn the basics of writing queries using GraphQL

GraphQL is often touted for its efficiency when consuming data from APIs. There are two key aspects to its efficiency:

  1. You get only the data you need. GraphQL will only respond with the fields you specify, meaning it does not 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.
  2. 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 from a single request. For example, I can get user information and their order information in a single query rather than hitting 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 to help you get started.

Getting Set Up

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:

This tutorial will use GraphQL Playground.

In order to use the API outside the API Explorer, you'll need to generate a Personal Access Token. If you choose to use GraphQL Playground, you'll first need to add the API endpoint of https://api.github.com/graphql. If you try to run anything, you'll get a 401 error though. To fix this, add an authorization header in the HTTP headers (located on the bottom left side of the application window):

{
  "Authorization":"Bearer MY_TOKEN"
}

HTTP Headers in Playground

Once the HTTP headers are set up, you should be able to click on the Docs tab on the far right to explore the types and queries available within the GitHub API.

Playground Schema docs

Ok. We're finally ready to start building a query.

Creating Your First Query

Let's start with a relatively simple query to find out how many people are following the topic of GraphQL on GitHub using the topic query. In a RESTful API situation, this might involve calling an endpoint such as /v3/search/topics which would return all the details we need. 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 will offer code hinting.

Let's start by calling the topic query:

{
  topic
}

You may notice that your GraphQL IDE indicates that there is an error in your request.

GraphQL query error

As the error indicates, the topic query expects a name argument of type string. So let's add our first argument, which is placed in parentheses after the query:

{
  topic(name: "graphql")
}

You'll note that we're still seeing an error. Why? Well, the way that GraphQL is able to supply you only the data you need is by requiring that you specify the fields you require within your query. There is no equivalent of select * in GraphQL, and this is for a number of good reasons. So, let's specify some fields within our query.

The topic query has 6 fields available, but really we're only interested in the number of people following it, or the stargazersCount field:

{
  topic(name: "graphql") {
    stargazerCount
  }
}

Finally, no errors. Let's run our query. Here's the response:

{
  "data": {
    "topic": {
      "stargazerCount": 12201
    }
  }
}

You'll notice that the response matches the query, meaning we don't need to waste time figuring out the structure of the response.

Arguments

In the above example, we had a single argument that was a string. Arguments can be any of the GraphQL basic types but they can also be an enum or even a custom object type. For example, GitHub's search query takes a searchType argument that is an enum with the possible values of ISSUE,REPOSITORY or USER.

{
  search(query: "GraphQL", type: REPOSITORY) {
    repositoryCount
  }
}

Meanwhile, for example, the repositories object within a user query takes a custom object argument of type RespositoryOrder that specifies both the field to order by and the direction in which to order. Here's a snippet from a larger query we'll look at in more detail later:

...
repositories(orderBy: {field:UPDATED_AT, direction: DESC}) {
...

The point here is that GraphQL allows APIs to support very complex querying capabilities in arguments.

Nested Objects

So far, we have a very simple query that gets only 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, let's get a list of related topics to GraphQL within GitHub using the related topics object.

{
  topic(name:"graphql") {
    stargazerCount
    relatedTopics {
      name
      stargazerCount
    }
  }
}

You'll see that we need to specify the object and then again the fields within that object that we 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 of first that defaults to 3, meaning it will only send the first 3 results. Let's define that to give us 10 (the maximum the API allows):

{
  topic(name:"graphql") {
    stargazerCount
    relatedTopics(first: 10) {
      name
      stargazerCount
    }
  }
}

As you can see, you can specify arguments not just on the primary query but on nested object queries as defined by the schema.

Aliases

Now let's imagine that we 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 would give you an error however because we have defined conflicting queries in a single request. To solve this, we need to use an alias on each of the queries:

{
  graphql:topic(name: "graphql") {
    stargazerCount
  }
  javascript:topic(name: "javascript") {
    stargazerCount
  }
}

This will return 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 imagine that we wanted to make this query get more than just the stargazerCount. We wouldn't want to have to repeat this code for each aliased query. This is where fragments come in. They can 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
    }
  }
}

...we can write this:

{
  graphql:topic(name:"graphql") {
    ...topicDetails
  }
  javascript:topic(name:"javascript") {
    ...topicDetails
  }
}

fragment topicDetails on Topic {
	stargazerCount
    relatedTopics(first: 10) {
      name
      stargazerCount
    }
}

We define the fragment and give it a name (topicDetails in this case). You must define the type it is a fragment on (Topic in this case) for validation purposes. Then we can reuse the fragment by referencing it's name prefixed by three dots (i.e. ...topicDetails in this case) wherever we need to reuse it. While we didn't save a ton of code in this example, this can be particularly useful when you find yourself repeating large chunks of code in a complex query.

Variables

Lastly let's discuss variables. Let's imagine I wanted to create a query that gets the user profile information and recent repositories for a user. This wouldn't work if I had to pass a hardcoded user string. Instead, GraphQL allows me to define a variable and then pass in a variables object to the query to populate the variables.

First, let's look at our query. This query basically gives us everything we 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
        }
      }
    }
  }
}

Not related to variables, but up until now we have been using an anonymous operation (i.e. wrapping the query in curly braces), but best practice says we should define the operation type and name. In this case, our operation type is a query and the name is GitHubUserProfile. There are other operation types such as a mutation, which modifies data on the server, and a subscription, which subscribes to updates to changes from the server.

We have 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 we now reference the $username variable as the value for the login argument.

To specify that argument, we need to pass in a JSON object containing a value for username. We can do that in GraphQL Playground via the variables tab in the bottom left of the application.

GraphQL variables

We can supply a GitHub username:

{
  "username": "remotesynth"
}

...and we can see the result of running the query on the right (I won't duplicate the result here as it is rather long).

Where To Go Next

If you are looking for more details, we recommend the following resources:

This site uses cookies: By using this website, you consent to our use of cookies in accordance with our Website Terms of Use and Cookie Policy.