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
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
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
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.js imports it, you need to delete line 3 from
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
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
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
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
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
This component accepts props
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
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
Now we keep track which repo is opened, using
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
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.
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
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:
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!