The three musketeers: Apollo Client, Graphql and React

You want to build a React application using Graphql (as a query language) and Apollo-client (as a query service and state management library)? Then stick around my friend, you are in the right place.

In this article, you can build your React app quickly and understand some of the basics of Graphql and Apollo-client. I assume that you already have a React project and you know React basics.

Okay so, First things first !!

Why do we even need an extra library besides React?

Let’s go back in time!! Don’t worry I am not gonna go back to 18th-century :D (I’ve always hated history subjects anyways)

In libraries like React ( Angular/ Vue / …), components are designed to transform data into UI. But that data has to come from somewhere (the sky for example :D ) an API, database, file… After fetching that data, we have to host it in our application’s cache.

Fetch data-> store data in the cache-> manage data in the cache-> transform that data into View UI. Simple right? Well yeah in smaller apps, but in bigger ones it gets messy, trust me.

I said before that React transforms the data (content) into UI (views). With the fetching and the caching, we are asking React to do more!! That’s why we end up with complex classes, too many files or files where we have to scroll 100 times to get to the bottom (I hate to scroll).

I think by now we’ve answered the question; Why do we need an extra library besides React? To have less complex classes. Daah !!

REST in peace :’)

To fetch data,the most used architecture is REST, which returns a JSON object. But REST is too binary, it returns all or nothing. Like when you have an endpoint “…/users” that returns all the users and all their attributes :

users:[
{firstName:"",lastName:"", gender:"",birthDate:"", email:"",...}
...
]

But you only need the firstName!! The solution here is to take what you need in the client-side and get rid of the others (more state management stuff), or you will add parameters to customize the SQL query. But sometimes you can’t, and let’s not forget about the endpoints ( is it /users or /clients I always get confused ). So what can we do about it? Well, there is Graphql.

Graphql is two things a query language and a type system, where you define your schema, then provide functions for each field on each type. Building the schema is extra work, but by defining it, you will prevent much more work afterward: fixing bugs…

Complicated !! here is a simple definition. Graphql is a tool based on REST with POST requests only. That’s all you need to know for now.

Now you will ask: What about GET and DELETE requests? It’s a POST request because you embed the query in its body so that you can customize what you want back as a result. Example :

query{
    users {
        firstName
    }}

Here you are telling Graphql to return all users’ firstNames. And you get as a result:

{data:{ 
  users:[
    {firstName: ""},
    {firstName: ""},
    {firstName: ""},
    ...
 ]
}}

Now to update or insert data you use mutation :

mutation{ 
 createUser(input:
       {
         name:"toto", 
         username:"toto", 
         email:"toto@gmail.com"})
       {
         id,  
         name, 
         username
      }}
/* create a client with name, username and email 
and return to me its id , name and username */

Cool right!! I still have a surprise, you have only one endpoint where you can request all your schema. This is getting better and better :D.

So for now, we have:

  • Queries: are SELECT statements.
  • Mutations: are INSERT / UPDATE / DELETE statements.
  • Subscriptions: real-time messaging.

Subscriptions are similar to queries but instead of immediately returning a single answer, a result is sent every time a particular event happens on the server. However, there is a technical difference between query and mutation: Query can be executed in parallel by the GraphqL engine while mutation top-level fields MUST execute serially.

We have the data we needed, now we have to manage it in our state, right?

For state management we have so many tools, the simplest is using HOC ( Higher Order Component). For a larger tree component, we can use React Context API. And there is the famous Redux. The concept of Redux is genius, you call the action which triggers the reducer and your store is changed.

When I started working with redux, I was excited, until I realized, I had to change 3 files to modify an object!!


Since we are using Graphql, we will use Apollo as a query service and as a state management library.

As a query language is it necessary? Well, you can just send strings to Graphql, but if you want more comfort and caching, the best choice is Apollo.

Apollo is a complete state management library for JavaScript apps. Simply write a GraphqL query, and apollo-client will take care of requesting and caching your data, as well as updating your UI.

So you won’t fall asleep here is some code (we developers love codes)

Let’s begin with the dependencies :

npm install apollo-client apollo-cache-inmemory apollo-link-http apollo-link-error graphql-tag

Those are a lot to add, yeah I know! luckily for us now and thanks to Peggy Rayzis we only need:

npm install apollo-boost react-apollo graphql
//for yarn users 
yarn add apollo-boost react-apollo graphql

Yep, that’s all you need, apollo-boost is a quickstart that assembles all the things you need to build your app, it contains:

  • apollo-client: Where all the magic happens.
  • apollo-cache-inmemory: The recommended cache from the official documentation.
  • apollo-link-http: An Apollo Link for remote data fetching.
  • apollo-link-error: An Apollo Link for error handling.
  • graphql-tag: To parse GraphqL query strings into the standard GraphqL AST.

GraphqL receives the query in string format. That string must be tokenized and parsed into a representation that the machine understands. This representation is called an Abstract Syntax Tree (AST). Don’t worry, we won’t have to do all this, it’s all done by “gql” tag.

Now let us begin:

/*Inside App.js import the things you just installed*/ 
import React  from 'react';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
/*Now create a new client with ApolloClient and give it the location of your uri.*/ 
const client = new ApolloClient({
// a fake graphql api 
    uri: "https://graphqlzero.almansi.me/api",
//optional 
    clientState: {
         defaults: {
            counter:0
         },
         resolvers: {}
   }
})/*Wrap your app with the ApolloProvider, also pass your client down to the next component.*/
function App(props) {
    return ( 
        <ApolloProvider client={client}>
         <div> 
           <h2>My First Apollo app </h2> 
         </div> 
        </ApolloProvider>
); }
export default App;

Now, of course, nothing happens yet. Let’s build our first query component. Apollo gives you a UI component that supports the query and synchronizes your data query with the UI :

import React from 'react';
import {gql} from 'apollo-boost';
import { Query } from 'react-apollo';
/* the query returns all users: email, name , username*/
const GET_ALL_USERS = gql`
  query {
    users {
      data {
        email
        name
        username
      }}
  }`;
export default  () => {
  return (
    <div>
      <Query query={GET_ALL_USERS}>
        {({ loading, error, data }) => {
          if (loading)
            return (
              <img src="http://www.fatihoglu.com/_frontend/ux/images/header/loader.gif" width="50px"/>
            );
          if (error)
            return (
               <h3> `Error!: ${error}`</h3>
            );
          return (
            <div>
              {data.users.data.map((item, index) => (
                <div>
                <h3>
                    {index+1}. User Email : {item.email}
                    User name : {item.name}
                    </h3>
                </div>
              ))}
            </div>
          );
        }}
      </Query>
    </div>
  );
};

Query component supports:

  • query: the query wrapped in gql-tag.
  • variables: parameters for the query.
  • client: the client.

In these examples, the Apollo query uses the Client (URI) passed in ApolloProvider. But if we want to use another client we can pass it to the Query component.

To pass parameters to your query. For example, if you want to get a user by its id :

import React, { useState } from 'react';
import {gql} from "apollo-boost";
import { Query } from "react-apollo";
/* the query returns  user: email, username. by userId  */
const GET_USER_EMAIL = gql`
  query getuser($id: ID!) {
    user(id: $id) {
      email
    }
  }
`;
export default  () => {
  const [userId, setUserId]= useState(1);
  return (
    <div>
        <input type="text" onChange={(e)=>setUserId(e.target.value)}        value={userId}/>
      <Query query={GET_USER_EMAIL} variables={{ id: userId }}>
        {({ loading, error, data }) => {
          if (loading)
            return (
              <img src="http://www.fatihoglu.com/_frontend/ux/images/header/loader.gif" width="50px"/>
            );
          if (error)
            return (
               <h3> `Error!: ${error}`</h3>
            );
          return (
            <div>
              {data && data.user && 
                <h3>User Email : {data.user.email}</h3>
              } 
            </div>
          );
        }}
      </Query> 
    </div>
  );
};

We get 3 objects in return:

  • loading: true if data are still loading.
  • error: true if the request encountered an error.
  • data: your data.

Now it’s easier to add a loader and manage error messages.

A mutation is practically the same thing.

const ADD_POST = gql`
  mutation createPost($title: String!, $userId: String!) {
    createPost(title: $title, userId: $userId) {
      id
      title
    }
  }
`;
// your compoenent 
.. 
    <Mutation mutation={ADD_POST}... >
        ..

In the examples above, we are using React components ( Query, Mutation ). What if we want to use React hooks? Indeed most recent projects are using React hooks, this is why hooks were included in the react-apollo package in its latest versions.

Now let’s manage the counter’s state with useQuery.

import React, { Fragment } from 'react';
/* useApolloClient returns the client instance being used by the application: we passed've that client using ApolloProvider in app.js file */
import { useQuery, useApolloClient } from 'react-apollo';
import { gql } from 'apollo-boost';
/* You can add the query (COUNTER_QUERY) in a separated file , to avoid code duplication*/
const COUNTER_QUERY = gql`
  {
    counter @client
 }
`;export default ()=> {
    const { data } = useQuery(COUNTER_QUERY);
    const client = useApolloClient();
    const changeState = value =>
    // write state counter in your cache 
      client.writeData({
        data: { counter: value }
      });
  
    return (
      <Fragment>
        <button  onClick={() => changeState(data.counter+1)}>
          Increment counter 
        </button>
            <br/>
        <button  onClick={() => changeState(data.counter-1)}>
          Decrement counter 
        </button>
        <br/>
        {data && (
               <p>{data.counter }  </p>
        )}
        
      </Fragment>
    );
  };

Now we want to access that same object (counter) from a different component. In redux, we connect the component to the store, in Apollo we only need the gql query to access the cache. You can see now that the query is slightly different from before, we’ve added the tag @client to “counter” so that Apollo knows that the object is in our cache.

import React from 'react';
import { useQuery } from 'react-apollo';
import { gql } from 'apollo-boost';
const COUNTER_QUERY = gql`
  {
    counter @client
 }
`;
export const Countstate=()=>{
    const { data } = useQuery(COUNTER_QUERY);
    return (
        <div >
          
          {data && (
                 <p > count state : {data.counter }  </p>
          )}        </div>
      );
}

Now we want to access that same object (modalOpen) from a different component. In redux, we connect the component to the store, in Apollo you only need the gql query to access the cache. You can see now that the query is slightly different from before, we’ve added the tag @Client to “modalOpen” so that Apollo knows that the object is in our cache.

import React, { Fragment } from "react";
import { useQuery } from "react-apollo-hooks";
import { gql } from "apollo-boost";
import Typography from "@material-ui/core/Typography";
// you can export it from the other file 
const MODALS = gql`
  {
    modalOpen @client
  }
`;
export default () => {
  const { data } = useQuery(MODALS);
  return (
    <Fragment>
      {data && (
        <Typography variant="h1">
          Modal is {data.modalOpen ? "Open" : "closed"}
        </Typography>
      )}
    </Fragment>
  );
};

That is all. Now you can fetch your data from your server, play with it in your cache and let React do what it does best.

Conclusion

Using Graphql will help us prevent over fetching, have smaller payloads and fewer Server/ Client round trips. Wrapping GraphqL with apollo-client is a big plus for caching and managing requests, it simplifies a lot of tasks and makes your code cleaner and easier to read.

Apollo has many other tricks up its sleeve, let’s discuss them some other time. Thanks for reading

Git repo :  Enjoy!!