Implementing GraphQL in React using Apollo

John Kariuki

This is a second part of a set articles exploring GraphQL technology. Refer to Implementing GraphQL Using Apollo on an Express Server for the server setup which is necessary for this client side article.

In this section, we focus on how to implement GraphQL on a React client using Apollo. We begin by setting up a simple React Component. First we will have an app component which will be the parent component and another component to render the list of channels and a third component for creating new channels.

Project Structure.

We begin by defining a simple directory structure for our react application.

app/
β”œβ”€β”€ src/
    └── app/
              β”œβ”€β”€ components/
              β”œβ”€β”€ ChannelList/
                  └── ChannelList.jsx
              └──  CreateChannel/
                  └── CreateChannel.jsx
        β”œβ”€β”€ app.jsx
        β”œβ”€β”€ public/
            └── index.html
        β”œβ”€β”€ style/
            └── style.scss
        β”œβ”€β”€ .eslintrc.js
        β”œβ”€β”€ webpack.config.js
        └── package.json

Creating a ChannelList Component.

We create a simple functional component to show a list of channels from the server.

// src/app/components/ChannelList/ChannelList.jsx
import React from 'react';
const ChannelsList = () => {
  ...
 };

Wiring Our component with the GraphQL query.

To hook up our component with our query, we require the Apollo client and some other helper packages from react-apollo.

npm i -S react-apollo #yarn add react-apollo

react-apollo allows you decorate your components with a higher order component called graphql to get your GraphQL data into the component. React Apollo also comes with ApolloClient, which is the core of Apollo that handles all the data fetching, caching and optimistic updates. To setup the Apollo Client, we will need to import the required components.

// src/app/components/ChannelList/ChannelList.jsx
...
import {
  gql,
  graphql
} from 'react-apollo';
...

Next, we decorate our component with a GraphQL higher-order component that takes the query and passes the data to the component:

// src/app/components/ChannelList/ChannelList.jsx
...
const channelsListQuery = gql`
   query ChannelsListQuery {
     channels {
       id
       name
     }
   }
 `;
const ChannelsListWithData = graphql(channelsListQuery)(ChannelsList);
export default ChannelsListWithData;

The gql template tag is what you use to define GraphQL queries in your Apollo Client apps. It parses your GraphQL query which may then be consumed by Apollo Client methods. Whenever Apollo Client is asking for a GraphQL query you will always want to wrap it in a gql template tag.

When wrapped in the graphql HOC, our ChannelsList component will receive a prop called data, which will contain channels when it’s available, or error when there is an error. Data also contains a loading property, which is true when Apollo Client is still waiting for data to be fetched; this reduces the amount of code which we would otherwise write to monitor the state of the request. We can utilize the data in out component to notify the user accordingly about the state of the API request. We can then render the channels if they are fetched successfully.

// src/app/components/ChannelList/ChannelList.jsx
...
const ChannelsList = ({ data: {loading, error, channels }}) => {
   if (loading) {
     return <p>Fetching Data...</p>;
   }
   if (error) {
     return <p>{error.message}</p>;
   }
   return <ul className="list-group">
     { channels.map( channel => <li class="list-group-item"key={channel.id}>{channel.name}</li> ) }
   </ul>;
 };
 ...

Setting up the Main App Component.

We then set up the main app component that will render the ChannelList component.

The app component is responsible for setting up the Apollo client and a network interface which defines the a http connection of our API.

// src/app/app.jsx
import {
 ApolloClient,
 ApolloProvider,
 createNetworkInterface
} from 'react-apollo’;
import ChannelList from './components/ChannelList/ChannelList';

const networkInterface = createNetworkInterface({
  uri: 'http://localhost:7700/graphql',
});

const client = new ApolloClient({
   networkInterface,
});

let app = document.querySelector('#app');

render(
    <ApolloProvider client={client}>
      <div className="App">
        <h3 className="center">React , GraphQL , Apollo</h3>
        <div className="row">
            <div className="col-lg-4 col-lg-offset-4">
                <ChannelList />
            </div>
        </div>
      </div>
    </ApolloProvider>,
    app
)

The createNetworkInterface() function creates a simple HTTP network interface using the provided configuration object which includes the URI Apollo will use to fetch data from. The ApolloProvider HOC provides a client instance to a React component tree, similar to react-redux Provider component. It provides an ApolloClient instance to all of your GraphQL components that use the graphql function. If you do not add this component to the root of your React tree then your components enhanced with Apollo capabilities will not be able to function.

A part from the client prop, ApolloProvider may also take an optional store which is an instance of Redux if we wish to include Redux in your project too. This ensures that we only use one HOC ApolloProvider and not react-redux Provider.

When we navigate to http://localhost:7700/graphiql and issue our query, we see the following results.

Subsequently when we open our application at http://localhost:7800/, we can confirm the expected results.

Performing GraphQL Mutations.

Mutations are aimed at creating, updating or deleting of records. They basically perform the write operations equivalents in CRUD operations. In this section, we will create a mutation that creates a new channel. We will create a new component CreateChannel that will be responsible for creating the new channels. It’s a simple component that provides an input field and an event handler for the input.

// src/app/components/CreateChannel/CreateChannel.jsx
...
const CreateChannel = ({mutate}) => {
    const handleKeyUp = (evt) => {
      if (evt.keyCode === 13) {
        evt.persist();
        mutate({
          variables: { name: evt.target.value }
        })
        .then( res => {
          evt.target.value = '';
        });
      }
  };

  return (
    <input
      type="text"
      className="form-control"
      placeholder="New channel"
      onKeyUp={handleKeyUp}
    />
  );
};

const CreateChannelMutation = gql`
  mutation addChannel($name: String!) {
    addChannel(name: $name) {
      id
      name
    }
  }
`;

const CreateChannelWithMutation = graphql(
  CreateChannelMutation
)(CreateChannel);

export default CreateChannelWithMutation;

Using graphql with mutations makes it easy to bind actions to your components. Unlike queries, which provide a complicated object with lots of metadata and methods, mutations provide only a simple function to the wrapped component, in a prop called mutate. Most mutations will require arguments in the form of query variables.

...
mutate({
   variables: { name: evt.target.value }
})
...

In the above snippet, we pass a name variable into mutate which will be passed to the actual query and will send to the server. The addChannel query below takes in the name variable passed in as part of the variables in mutate above.

...
const CreateChannelMutation = gql`
  mutation addChannel($name: String!) {
    addChannel(name: $name) {
      id
      name
    }
  }
`;
...

We then wire it up in the main app component.

// src/app/app.jsx
…
import CreateChannel from './components/CreateChannel/CreateChannel’;
… 
render(
    <ApolloProvider client={client}>
      ...
        <div className="row">
            <div className="col-lg-4 col-lg-offset-4">
                <CreateChannel /><br/>
                <ChannelList />
            </div>
        </div>
      ...
    </ApolloProvider>,
    app
)

To make our input component call a GraphQL mutation, we have to wire it up with the GraphQL higher order component (HOC) from react-apollo. For mutations, the graphql HOC passes down a mutate prop, which we’ll call to execute the mutation. Inputting a new value and reloading the page shows the new channel.

Conclusion.

GraphQL is a pretty alternative to the conventional REST implementations. Looking at the above application and making a comparison with Redux, we can see that the amount of code used is reduced by a very large percentage. We can avoid the whole concept of writing actions and reducers and configuring stores.

In the next and final part of this series, we will focus on how to update the UI in realtime; how to perform operations and have the browser automatically update in three different ways; refetching queries, manually updating the client state based on the mutation result and using GraphQL subscriptions to notify the client about the updates. Unlike conventinal REST updates, GraphQL provides a more real-time view using subscriptions; updating data in one browser will automatically show changes in the next browser like push notifications.

John Kariuki

19 posts

Software developer at Andela. Proficient in PHP with Laravel and Codeigniter.

Conversant with MEAN(MongoDB, Express.js, AngularJS, Node.js) and currently learning Python and Go.

Avid blog reader and fascinated by drones.

I play basketball, swim and jog in my free time.