Last year, I started using GraphQL at my job. I decided to create some GraphQL prototypes in my spare time, to get better acquainted with the GraphQL ecosystem. In 2018 I learned the basics of GraphQL and wrote two articles about my experience, but never dove into using GraphQL in real world applications. The GraphQL React prototype discussed in this article along with my Apollo prototype are the beginnings of that production application journey. In the future, I plan on using GraphQL for the API layer of my applications.
The GraphQL prototype discussed in this article is a React front-end application that connects to a GitHub GraphQL API. The API provides details about my repositories, and React displays those details in a dashboard. The dashboard is shown below.
The dashboard has a responsive design, so its also viewable on tablets and mobile screens.
The React code for the dashboard is broken down into multiple components, with each box displayed on the screen being its own component. Components use Less for their stylesheet language. Less, similar to Sass, is a CSS preprocessor, which adds features on top of the base CSS language.
Each component makes a GraphQL query to the GitHub API, collecting the data it wishes to display. The front-end code doesn't use a GraphQL client library to make the API calls, instead just using an HTTP client library called
axios. In an upcoming article I'll discuss my Apollo prototype, which uses the Apollo Client library to make API calls. While the HTTP client library is a bare-bones approach, it is still fully capable of handling GraphQL APIs.
Let's look at one of the basic components displayed on the UI. The
RepositoryCount component has the responsibility of displaying the number of repositories in my GitHub account on the dashboard. It has a RepositoryCount.js file for the React component code and a RepositoryCount.less file for the stylesheet code.
RepositoryCount component makes a GraphQL API call to the GitHub API after the component first renders. Once a result is returned from the API, the component re-renders with the number of repositories in my GitHub account.
The repository count is held in a
useState React hook with the name
repoCount. This value is changed using the
setRepoCount function. When
setRepoCount is invoked, the
RepositoryCount component re-renders and displays the repository count in an HTML
<h2> element. If an error occurs while making the API call, the error message is stored in the
error variable, which is also managed by a
useState React hook. If the
error variable contains a string value, it is displayed in an HTML
useEffect React hook is invoked once after the component first renders. It's purpose is to make the GitHub API call and store its response in a state variable. The
getGraphQLResult() function helps performs this task. The first line of
getGraphQLResult() calls a
getPersonalRepositories() function, which is imported from another file. I extracted all the API client logic into a separate file, which I will discuss later. The API call is asynchronous, which is why I use the
getGraphQLResult() handles the response in the case of a success or failure.
If the API call is successful and the response object contains data, the repository count is assigned to the
repoCount variable using the
setRepoCount function. If the API call is unsuccessful and contains no data, the API returns an error message. This error message is assigned to the
error variable using the
As previously mentioned, the API client logic is encapsulated in a separate file called GraphQL.js.
getPersonalRepositories(), which is used in the
RepositoryCount component, is defined as follows:
getPersonalRepositories() takes a GitHub username as an argument (in my case AJarombek) and then invokes a
request() uses the axios HTTP client library to make the GraphQL API request to the GitHub API.
As you can see, both the GraphQL query and the query variables are passed in an HTTP POST body to https://api.github.com/graphql.
The final piece of the puzzle is the GraphQL query. The first argument to
request() in the
getPersonalRepositories() function body was a variable named
getPersonalRepositoriesQuery. This variable is a string containing a GraphQL query.
All the code I've shown, from the
RepositoryCount component to the axios client, executes a GraphQL query and renders the result in a web browser.
For additional perspective, shown below is the GraphQL query executed from an HTTP UI client. In this case I'm using Insomnia, but you can use Postman or any other client for HTTP requests. The important point is that GraphQL queries aren't doing anything too complex, they are simply HTTP requests.
Let's briefly look at one more component. The
TotalCommits component displays the five repositories with the most code commits in my GitHub account.
TotalCommits has a TotalCommits.js file for the React component code and a TotalCommits.less file for the stylesheet code.
TotalCommits is a bit more complex because it needs to manipulate the data received from the GraphQL API, and then display the data in a list.
In many ways,
TotalCommits is structured the same way as
RepositoryCount. It renders either an error message or data from the GitHub API. It utilizes
useEffect() React hooks to retrieve and store the API data after the components initial render. There are two main things that differentiate
RepositoryCount. First, it restructures and sorts the data retrieved from the API in a separate function called
repoCommits variable, representing the five repositories in my GitHub account with the most commits. Second, the component renders each of these repositories in their own HTML
<div> element, displaying the repository name and the number of commits.
TotalCommits invokes a
getTotalCommits function to retrieve the repository information from the GitHub GraphQL API. This function exists in the GraphQL.js file. The GraphQL query for this component is shown below, invoked from the Insomnia HTTP UI Client.
All the components in my GraphQL React prototype follow a similar structure to
RepositoryCount. The rest can be viewed on GitHub.
The GraphQL React prototype uses Kubernetes infrastructure hosted on AWS. This infrastructure is built with Terraform, and has the following structure:
There are three Terraform modules for the infrastructure:
acm for ACM certificates,
ecr for ECR repositories containing Docker images, and
k8s for the Kubernetes infrastructure. They exist in acm, ecr, and k8s directories, respectively. The Terraform infrastructure is built and torn down in Jenkins pipelines, specifically create-acm-infrastructure, create-ecr-infrastructure, create-k8s-infrastructure, destroy-acm-infrastructure, destroy-ecr-infrastructure, and destroy-k8s-infrastructure.
The Kubernetes infrastructure for the GraphQL React prototype is built with two Docker images. Their Dockerfiles are defined in the GitHub repository. The first image is a base image, containing all the npm dependencies needed by the React application. This Docker image is defined in a base.dockerfile file.
The second image is an application image. It builds the React application and creates an nginx proxy to route traffic entering the container to the application. This Docker image is defined in a app.dockerfile file.
The nginx proxy has the following configuration, defined in a nginx.conf file.
I don't discuss the infrastructure any further in this article, but I have lots of articles on Terraform, AWS, Kubernetes, Docker, and Jenkins if you want to learn more about the underlying technologies.
The GraphQL React prototype has unit tests and snapshot tests with full code coverage. Tests exist in the test directory and are written with Jest. I wrote an article about writing integration, unit, and snapshot tests for React with Jest if you want to learn more.
Jest unit and snapshot tests for the GraphQL React prototype are run in Jenkins on a daily schedule. The Jenkins pipeline code for these tests exists in my global-jenkins-jobs repository.
Creating this GraphQL React prototype was a great exercise and helped me learn how to use GraphQL in a frontend application. However, I likely won't repeat the technology choice of using an HTTP client library instead of a GraphQL client library for front-ends dealing extensively with GraphQL APIs. In many ways, I made things more difficult for myself by using axios instead of a GraphQL library. I have another prototype application which uses the apollo GraphQL client library with React, and it made integrating GraphQL with React quite a bit easier.
All of the application code discussed in this article is available on GitHub.