GraphQL is a query language by Facebook, which became very popular as a replacement for REST. In this tutorial, we will build a React application that pulls data from Github using GraphQL.
If you have not heard of GraphQL at all, I suggest you start with this article. It explains the differences between GraphQL, REST and SQL, as well as introduces the terminology.
What we will build
We will build a simple React app that will look up a repository by its name and display some information about it. Here is how it will look when we finish the tutorial:

Step 1. Generate an access token
Before we dive into code, we need to create an access token. An access token is used to authorize the API calls and it required for any communication with Github. To get it, open https://github.com/settings/tokens and press Generate new token:

For our purposes, you will need the public_repo
scope, others are optional. Once you create your token, copy it someplace safe, we will need it later.
Step 2. Create a new React app
We will use React to build our app. To create it, run this command in the command line:
npm init react-app react-graphql-client
After it finished creating the app, we need to install some dependencies. We will use material-ui
as a component library and apollo-boost
, @apollo/react-hooks
and graphql
for GraphQL communication:
npm i --save @material-ui/core @material-ui/icons apollo-boost @apollo/react-hooks graphql
Step 3. Secure the access token
We will be referencing the Github API token throughout the app, but it is not a very good practice to put it directly in the source code. Also, you should never have API keys in your VCS. To solve this problem, a .env
file is used. Create a .env file in the project root and put this line in it:
REACT_APP_GITHUB_KEY = your_key_goes_here
This line will make REACT_APP_GITHUB_KEY
be injected as an environment variable into your app. It will be available as process.env.REACT_APP_GITHUB_KEY
. Make sure not to commit this file!
Step 4. Download the schema
One great advantage of GraphQL is syntax highlighting and strong typing. To enable syntax highlighting for your IDE, create a file called .graphqlconfig and put this content in it:
To make use of this file, you will need to install an extension. If you are using WebStorm, it is called JS GraphQL:

For VS Code, just GraphQL:

After you install it, it will prompt you to download the schema defined in the .graphqlconfig
file. With the schema, the syntax autocompletion is working. One important note: you see you need your API token to access the schema. That is not a perfect solution, and it also means you should not commit .graphqlconfig as well.
Step 4. Define the queries
Before we start writing React code, let’s stop for a moment and think what GraphQL queries we will need. A query is an expression, written in GraphQL language, that is used to retrieve data from the server (from Github, in our case). You can find the list of supported queries on Github API Reference.
Well, we probably want to search for repositories by name and get their description and link. The search
query will be suitable for it. It will also be nice to be able to view open issues for repo, and it is done with the repository
query.
Create a file queries.js
in the src
folder and put this code in it:
We begin by importing the gql
tag from graphql-tag
. It helps format and parse GraphQL queries. On lines 3-23 we define our first query, the SEARCH_FOR_REPOS
. It accepts search_term
as an argument (line 5) and will return first 20 matching repositories. The response will include the total number of matching repositories (line 6), the name of every repo (line 10), the username of every owner (lines 11-13) the number of stars (lines 14-16) and the description (line 18).
Our second query, the GET_REPO_ISSUES
will look up the first 20 issues for a repository by its name and owner. It will also sort these issues by date, new to old. The response includes the title of the issue, its body, and the timestamp (lines 30-32).
Sidenote: test your queries online
While our app is not ready yet, you can already test your queries using Github GraphQL Explorer. For example, by calling our first query with search_term: "React"
you will get this response:
Step 5. Create a search bar
Now should be the right time to start working on the UI. Our app will have: (1) a title, (2) a search bar to search for repos, (3) a list of repos, (4) when you click a repo, it expands and shows you the last 20 open issues. Let’s start by deleting everything in src folder, except index.js
, App.js
and serviceWorker.js
. Then, create a SearchBar.js
and put this code in it:
If you are not familiar with material-ui
, feel free to read its docs first. In this component, we render a simple text input with a search icon and some rudimentary styling. Note that SearchBar
is a controlled component: it accepts its value from props and fires onChange
callback as you type.
Note: since we deleted index.css
and index.js
imports it, you need to delete line 3 from index.js
.
Step 6. Create a repository list
Now that we have a search bar, let’s write a repository list. Create a RepositoryList.js
file and put this code in it:
We do not have any actual data yet, so we just let the user know there are no repositories at the moment. Before we proceed any further, it is worth putting everything together to make sure we are on the right track. Open App.js and edit it like this:
In it, we just render a title and our two components. To open the app, run npm start
from the terminal in the project root. Once it compiles, you should see this:

Congratulations, we are halfway there!
Step 7. Set up Apollo
Before we make any API requests, we need to wrap our app with an Apollo Provider. Apollo is a client library for GraphQL which provides bindings for React. A Provider will supply the Apollo client to every component in our app, making it much easier to use. It works similarly to the Redux Provider, if you are familiar with it. To add a Provider, we need to create a client first. Make a new file client.js
with this code in it:
This code will create an Apollo client that connects to Github API. Notice how we used the environmental variable to pull the API token on line 6. Because of that, this file is 100% safe to commit to VCS.
Now, we will use this client with a provider in App.js
:
You will not notice any visual differences at this point, so do not panic and proceed to the next step.
Step 8. Query repositories
Finally, it is time to do some actual queries! To do this, a new dependency is required, use-debounce
. It solves the problem of debouncing, i.e. makes sure you do not call the API on every keypress (that would be inefficient) but limiting it to some timespan. To install it, run this command in the project root:
npm i --save use-debounce
Now, we need to grab the search term from the SearchBox
and pass it to the RepositoryList
. This is done in App.js
:
Now, using this term, the RepositoryList
can perform queries. Update RepositoryList.js
like this:
Firstly, note the use of useDebounce
on line 21. It accepts two arguments: the value we want to debounce and the time interval. It returns a value that will be updated at most every 1 second (feel free to play around with the time interval).
On lines 22-24, we use the useQuery
hook to get data from the Github API. It accepts our SEARCH_FOR_REPOS
query, defined in step 4, and an object with options. In our case, we need to pass arguments to the query, the search_term
. useQuery
will re-run the query when these change, so you do not have to worry about it.
useQuery
returns an object with many fields, but we are interested in data
, loading
, and error
. These are pretty much self-explanatory, you can see how we use them on lines 26-45. You can explore the data structure by logging data
to the console, using the Github API Explorer, or installing the Apollo DevTools extension.
Step 9. Render repository info
Now we have the data and are able to render it. For each repository, we want to show its name, owner, and description. To do it, we will need a new component, Repository
. Create it in Repository.js
:
This component accepts props repo
, expanded
and onToggled
. repo
is the object with repository data, expanded is true if this component is expanded (to view issues) and onToggled
is a callback that will be fired on expanding/shrinking the component.
On line 32 you can see how repo is unpacked into name
, descriptionHTML
, login
, and totalStarCount
. These are later used to render the info. Since descriptionHTML
is an HTML string, we use dangerouslySetInnerHTML
on line 51 to render it.
Note: never use dangerouslySetInnerHTML
in production without sanitizing/checking it first. If it comes from an untrusted source, the third party could get full control of your website. We use it just for educational purposes.
To display the Repository
component, we need to make some adjustments to the RepositoryList
in RepositoryList.js
:
Now we keep track which repo is opened, using expandedRepo
and setExpandedRepo
on line 22. The effect on lines 28-20 resets it every time the data
changes, to make sure the repos shrink when we make a new search.
On lines 67-74 we iterate through the data.search.edges
, which contains an array of repositories. For every repository, a Repository
component is rendered with all the required information. Now your app should look like this:

Almost done, just issues left to go!
Step 10. Render issues
This is going to be very similar to how we render repositories, so I am going to go a bit faster here. To get issues we need to make another API call, the GET_REPO_ISSUES
from step 4. Then, display them in a list and show description on click. Create files IssueList.js
and Issue.js
with this code:
You see the familiar call to useQuery
, just with a new query and variables. Note that we do not need debouncing here as we are not dealing with user input. Once data
is fetched, we check that there are no errors and data
is not empty. If everything is fine, iterate over data.repository.issues.nodes
and render an Issue
component for every issue.
In the Issue
component, a ListItem
and a Dialog
are rendered. ListItem
has the title of the issue and it opens the dialog on click. The dialog has the HTML code of the issue body, the same as you could see on Github.
Now you just need to render IssueList
in Repository.js
:
Note the conditional rendering of IssueList on line 56. This is done for optimization so that the API request is only made when user opens the repo.
Here is the final look of your app:



Ending notes
Thank you for making it all the way to the end, I hope you liked my GraphQL tutorial. The code is available on my Github. If you are wondering what you could improve this app, here are some suggestions:
- Ability to open the respective Github pages
- View comments on issues
- Leave comments on issues
- View the readme.md
- And more!