React

GraphQL APIs are the future, and Apollo is the best way to integrate it into your React app right now.

React and GraphQL are a match made in heaven. GraphQL on its own is a blessing if you’ve ever had to make multiple requests to a RESTful API just to piece together all the data needed for an interface, but when combined with Apollo + React, it makes building interfaces very enjoyable.

In this article, we’ll start by getting a simple demo up and running with a library called Apollo Boost, where most settings are configured for us and we can be up and running quickly with very little fuss. After we’ve got that covered, we’ll remove Apollo Boost and configure the Apollo Client ourselves, learning how we can truly customize each GraphQL operation using links with Apollo Link.

For this demo, we will be querying our starred repositories within GitHub. The final version of the app can be found here. I encourage you to follow along on your own, but if you get stuck you can definitely use it as an example.

Browsing GitHub’s Graph

Apart from Facebook, GitHub is one of the pioneers in exposing a public GraphQL API. GitHub comes with an API explorer, allowing us to explore their graph and see what data we have access to, along with how each field is formatted and its data type. After connecting to the explorer, feel free to paste in the query below to see what data is returned.

 
{
  viewer {
    id
    starredRepositories(last: 25) {
      nodes {
        id
        name
        description
        pushedAt
        url
        languages(first: 5) {
          nodes {
            id
            color
            name
          }
        }
      }
    }
  }
}

Once you’ve done that, head on over to a page where you can generate a personal access token. For this demo, you’ll only need to give the token access to the repo setting. Copy and paste this token to store it for use later on.

Getting Started

Now that we’ve seen the query we’ll be working with and the data it returns, it’s time to make it work for ourselves within React using create-react-app. After installing create-react-app, create a new project using the command create-react-app name-of-app. Then remove the CSS and image files that come by default inside of the src folder.

Run the command yarn add apollo-boost react-apollo graphql graphql-tag @progress/kendo-react-inputs @progress/kendo-react-intl @progress/kendo-theme-default to install the packages needed to get going. I have also included a few packages from Kendo UI, which we’ll use for an input field in the app.

In the index.js file you’ll want to replace the CSS import currently there with one to import the Kendo UI theme: import "@progress/kendo-theme-default/dist/all.css";

Apollo Boost

We first need an Apollo Client. This is the code in charge of performing the GraphQL HTTP request, setting the headers, parsing the response, dealing with cache, etc. We’re going with a fairly basic setup here using apollo-boost, which is a library that provides you with the most common Apollo Client setup.

Even with apollo-boost we can still provide some customization. Here we are using the request option that allows us to dynamically set headers that will be sent with each request. This is typically where you’d handle authorization headers to include the user’s token. In this case, we are grabbing the value from sessionStorage (we’ll show how it got there just below).


// src/apolloClient.js
 
import ApolloClient from "apollo-boost";
 
const client = new ApolloClient({
  request: operation => {
    operation.setContext({
      headers: {
        Authorization: `bearer ${sessionStorage.getItem("token")}`
      }
    });
  }
});

<pre language-js"=""><code language-js"="">export default client;


Next we need an Apollo Provider, which will give our React app the ability to perform queries and mutations. You can only perform queries and mutations inside of the ApolloProvider component, so you’ll typically want to have this as high up the component tree as possible.

In the App component we also set up the app’s state, loading it from sessionStorage on the componentDidMount lifecycle function, and creating a setToken function we’ll pass down to a child component.

// src/App.js
 
import React, { Component } from "react";
import { ApolloProvider } from "react-apollo";
import apolloClient from "./apolloClient";
import StarredRepos from "./StarredRepos";
import TokenForm from "./TokenForm";

class App extends Component {
  state = {
    token: null
  };
  componentDidMount() {
    this.setState({ token: sessionStorage.getItem("token") });
  }
  setToken = token => {
    sessionStorage.setItem("token", token);
    this.setState({ token });
  };
  render() {
    const { token } = this.state;
    return (
      <ApolloProvider client={apolloClient}>
      <h1>Starry Eyed</h1>
      {token ? <StarredRepos /> : <TokenForm setToken={this.setToken} />}
      </ApolloProvider>
    );
  }
}
 
export default App;

We will need to provide the user a way to enter their GitHub token. Think of this as the user performing a login action, which would result in some kind of token that can then be sent with all subsequent requests to the server. In our case we’ll just show an input for the user to enter their personal use token from GitHub, which is the token you generated above.

import React from "react";
import PropTypes from "prop-types";
import { Input } from "@progress/kendo-react-inputs";
 
export default class TokenForm extends React.Component {
  static propTypes = {
    setToken: PropTypes.func.isRequired
  };
 
  handleSubmit = event => {
    event.preventDefault();
    const { setToken } = this.props;
    // accessing the value set with the `ref` prop on the `Input` component
    const token = this.tokenInput.value;
    if (token) {
      setToken(token);
    }
  };
 
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
      <Input
      name="token"
      placeholder="Enter your GitHub token"
      ref={input => {
        this.tokenInput = input;
      }}
      />
      </form>
    );
  }
}

 

Executing a Query

After the user has entered their token and we’ve put it both into the App state plus sessionStorage, it’s time to perform our first GraphQL Query! Let’s break the Repository component into three sections. The imports are first:


import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";
import Repository from "./Repository";
Next we’ll write our GraphQL Query:
const STARRED_REPOS_QUERY = gql`
  query StarredReposQuery($numRepos: Int) {
    viewer {
      id
      starredRepositories(last: $numRepos) {
        nodes {
          id
          name
          description
          pushedAt
          url
          languages(first: 5) {
            nodes {
              id
              color
              name
            }
          }
        }
      }
    }
  }
`;

 

You’ll notice that this query looks eerily similar to the one we were playing around with in the GitHub GraphQL explorer. That’s because it is! The first difference is that we’ve added query StarredReposQuery($numRepos: Int) to the front of it, telling Apollo we’ll be performing a query and giving it a name. This also tells the query that we’ll be providing an Integer variable named numRepos. I could have hard-coded this, but it shows how you can pass variables into your query to make them dynamic.

Finally, we have our actual component. Here’s where things get interesting! The Query component provided by react-apollo is the star of the show. As a child, it needs to be provided with a function that receives a number of arguments, but for our sake here we’ll be working with the data returned by the query and a loading variable, which is a Boolean that tells us whether the request has finished or not.

export default class StarredRepos extends React.Component {
 
render() {
  return (
    <div>
    <Query query={STARRED_REPOS_QUERY} variables={{ numRepos: 25 }}>
    {({ data, loading }) => {
      if (loading) {
        return <span>Loading...</span>;
      }
        return data.viewer.starredRepositories.nodes.map(node => ( <Repository data={node} key={node.id} />
      ));
    }}
    </Query>
    </div>
    );
  }
}

 

To finish off the app, let’s look at the Repository component used above. This is straight-up React with no fancy GraphQL stuff going on here. I like to separate where I fetch the data from where I display it. It makes it easier to test the component because there are no dependencies. It simply receives a data object and displays it.

import React from "react";
const Repository = ({
  data: { name, description, url, pushedAt, languages }
}) => (
  <div>
  <h2>{name}</h2>
  <a href={url} target="_blank" rel="noopener noreferrer">
  {url}
  </a>
  <p>
  <small>
  <em>{pushedAt}</em>
  {" / "}
  <strong>{languages.nodes.map(node => node.name).join(", ")}   
  </
strong>
  </small>
  </p>
  <p>{description}</p>
  </div>
);
 
export default Repository;

 

Apollo Client in Detail

But what if we reach the limits of apollo-boost or just want to see how we’d accomplish the same Apollo Client on our own? That’s what we’ll look at here.

Let’s add all of the packages we need—there are a lot of them! yarn add apollo-cache-inmemory apollo-client apollo-link apollo-link-context apollo-link-error apollo-link-http

Here’s a high-level overview of what we’ll use from the packages above:

  • ApolloClient: The ApolloClient is the main library that comprises everything done on the frontend relating to GraphQL. It is frontend-framework agnostic and can be paired with specific packages for React, Vue, Angular, etc. When working with ApolloClient, you can set link and cache as some of the options, which we will cover below.
  • InMemoryCache: When a GraphQL Query operation is performed, the resulting data is stored in a data store (think Redux or MobX). This is useful if the same information is requested that we already have — a round-trip to the server isn’t required and it can be served directly from cache. InMemoryCache is the default cache used in the apollo-boost library as well. There are a number of common use cases in which you would want to access the cache directly.
  • ApolloLink: ApolloLink allows you to compose links together. These act as middleware and allow you to control the flow of a GraphQL operation request.
  • onError: The onError function allows you to catch errors that have occurred during the GraphQL operation. Usually I use this area to notify the error reporting service being used (such as Bugsnag). In the code below, we will simply log it to the console.
  • setContext: This function takes a function that allows you to return the new context of the request. This is a perfect place for setting Authorization headers.
  • HttpLink: HttpLink is responsible for making the actual HTTP request. HttpLink relies on having fetch available, which isn’t a problem in modern browsers but is something to keep in mind if you wish to support older browsers. This is a terminating link so should be the last one in the chain.
import { InMemoryCache } from "apollo-cache-inmemory";
import { ApolloClient } from "apollo-client";
import { ApolloLink } from "apollo-link";
import { HttpLink } from "apollo-link-http";
import { setContext } from "apollo-link-context";
import { onError } from "apollo-link-error";
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, path }) =>
    console.log(`[GraphQL error]: Message: ${message}, Path:          ${path}`)
    );
  }
  if (networkError) {
    console.log(
      `[Network error ${operation.operationName}]: ${networkError.message}`
    );
  }
});

const authLink = setContext((_, { headers }) => {
  const context = {
    headers: {
      ...headers,
      Authorization: `bearer ${sessionStorage.getItem("token")}`
    }
  };
  return context;
});
const httpLink = new HttpLink({ uri: "https://api.github.com/graphql" });

// Finally once we’ve set up all our links, we can pass them to the ApolloClient
// using the ApolloLink.from function

const client = new ApolloClient({
  link: ApolloLink.from([errorLink, authLink, httpLink]),
cache: new InMemoryCache()
});
 
export default client;

 

Wrap-up

In this article we’ve covered some of the basics of how to use GraphQL in React using the Apollo library. We started off using Apollo Boost to configure our Apollo Client, but then went over how to customize the Apollo Client using a series of links and by specifying which cache we’d like to use. I recommend starting with Apollo Boost and only switching to your own configuration once you feel that it is limiting you. This is still just the tip of the iceberg in terms of what you can do with the Apollo GraphQL library, but it gives a good glimpse into its power and elegance to build data-driven interfaces in React.


For more info on building apps with React:

  • Check out our All Things React page that has a great collection of info and pointers to React information—with hot topics and up-to-date info ranging from getting started to creating a compelling UI.
  • You can also learn more about KendoReact, our native component library built specifically for React, and what it can do for you.

leigh-halliday
About the Author

Leigh Halliday

Leigh Halliday is a full-stack developer specializing in React and Ruby on Rails. He works for FlipGive, writes on his blog, and regularly posts coding tutorials on YouTube.

Related Posts

Comments

Comments are disabled in preview mode.