Learn GraphQL by Building a Github Client – Tutorial

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:

js graphql extension

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:

graphql github client screenshot

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:

graphql github client tutorial
Main page
graphql github client tutorial
Expanded the react repo
graphql github client tutorial
Viewing react issues

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!

Get new content delivered to your mailbox:

leave a comment