Consume a GraphQL API from a React Application

React is a JavaScript library used to create websites, it allows us to create Single Page Applications. The server will only send a single HTML page to the browser for the website to run fully, the react application manages the whole website including the data, user interaction, and routing.

Although React is a library rather than a language, it is widely used in web development. The library first appeared in May 2013 and is now one of the most commonly used frontend libraries for web development.

We will create a simple project that will teach us the fundamentals of React which include:

  • Keeping and tracking application state
  • Using a router to display pages/components
  • Fetching application data for the UI

Before following along I suggest that you at least know HTML & CSS, GraphQL, and JavaScript. We will use these technologies to simplify local development of a react application that uses a fake GraphQL API as its backend. The solution to be built is a Blog that lets us manage content.

Creating a React Application

The easiest way to initialize a react application is to use the CLI, you’ll need to have Node >= 14.0.0 and npm >= 5.6 on your machine. The tool will generate a starter react project which has all required configuration that's needed to compile the code to a production ready application.

Step 1: Initialize a React application

Navigate to the directory that you want to create the project and run the below commands:

npx create-react-app consume-graphql-api
cd consume-graphql-api
npm start
Create a starter project

Once you open the starter project folder in your chosen IDE (i.e. Visual Studio Code), you will see all the files that were auto generated by create-react-app. Lets do a brief overview of the folders and files:

  • All of the project's dependencies live in the node_modules folder, including the react library as well. Any other packages we need to install later on, will also be stored inside this folder.
  • Any files that are accessible by the browser are stored in the public folder. The index.html is stored here as well and will be served to the browser and all of the react application code will be injected by this file.
  • The application code will be stored in the src folder, this includes all components written in react. There's an app.js file that's already created and this is what a component looks like.
  • The index.js file takes all of the react components and mounts them to the DOM.
  • The package.json file lists all dependencies required to run the application and also provides scripts to perform certain actions for our project.

Step 2: Code refactor and Adding Components

The generated application has a default root component called App.js, it's the first component that gets rendered in the DOM. We will modify the component and add our custom styling:

import Home from './Home';
import Navbar from './Navbar';

function App() {
  return (
    <div className="App">
      <Navbar />
      <div className="content">
        <Home />
      </div>
    </div>
  );
}

export default App;
The root component contains multiple components

If we need more components (Navbar, PostDetail, SideBar) for our application, they will be nested in the App.js component. Let's create a Navbar.js component inside the src folder. This component will be placed at the top of the DOM:

const Navbar = () => {
    return (
        <nav className="navbar">
            <h1>Thabo's Blog</h1>
            <div className="links">
                <a href="/">Home</a>
                <a href="/create">New Blog</a>
            </div>
        </nav>
    );
};

export default Navbar;
The navigation will have links to other components

We need a Home component that will store thee home page content, it's possible to just place this content in the root component but that doesn't follow best practice. Lets create a Home.js component inside the src folder for the content of our home page:

const Home = () => {
    return (
        <div className="home">
            <h2>Homepage</h2>
        </div>
    );
};

export default Home;
The home component just has a title

This is how we create multiple components and import/nest them into other components.

Step 3: Address styling for the application

So we have our three different components (App, Navbar, and Home), we will now add some CSS to all these components. If you look closely to all the components we have created, we added the className in each file. We will use the index.css to style the application:

/* base styles */
* {
  margin: 0;
  font-family: "Quicksand";
  color: #333;
}
.navbar {
  padding: 20px;
  display: flex;
  align-items: center;
  max-width: 600px;
  margin: 0 auto;
  border-bottom: 1px solid #f2f2f2;
}
.navbar h1 {
  color: #f1356d;
}
.navbar .links {
  margin-left: auto;
}
.navbar a {
  margin-left: 16px;
  text-decoration: none;
  padding: 6px;
}
.navbar a:hover {
  color: #f1356d;
}
.content {
  max-width: 600px;
  margin: 40px auto;
  padding: 20px;
}
The CSS is applied globally to the application

We should see the styles applied to our application when we visit http://localhost:3000/ on the browser.

Step 4: Using Sate (useState hook)

Let's create a state to represent the blog posts, and to achieve we will use the useSate hook provided by react. The data for the posts might change overtime, so react will update the DOM when this happens.

The sate of a component refers to the data being used in the component, at that point in time. The state can be an array of values, booleans, strings, objects, or any data that a component uses.

Since we are building a simple blog, ideally we need to have a list of posts to output in a template. Inside the Home.js component we will display all the posts available, we will use dummy data for now:

import { useState } from "react";

const Home = () => {
    const [posts, setPosts] = useState([
        { title: "Software development", body: "The best programming...", author: "Thabo", id: 1 },
        { title: "Basic accounting rules", body: "Reconciliation is...", author: "Thabang", id: 2 },
        { title: "Soccer tournament", body: "Made it to the final...", author: "Nkosi", id: 3 }
    ]);

    return (
        <div className="home">
            {posts.map((post) => (
                <div className="blog-preview" key={post.id}>
                    <h2>{post.title}</h2>
                    <p>Written by {post.author}</p>
                </div>
            ))}
        </div>
    );
};

export default Home;
We have 3 items to display to the user

Now if everything is saved and the application is running, we should see the following on the browser:

List of dummy posts we created 

Creating a Fake GraphQL API

In the previous steps, we hardcoded our blog posts data. This is not ideal for web applications as we need our data coming from a database using an API endpoint. I will not deep-dive into creating a database and an API from scratch, feel free to checkout Generate a GraphQL API from a DynamoDB Table post.

Step 1: Using json-graphql-server

It is possible to create a full GraphQL API just using a javascript file that will store our data. The db.js file will act as our database, let's create this file in the root directory of our application:

module.exports = {
    posts: [
        { title: "Software development", body: "The best programming...", author_id: 1, id: 1 },
        { title: "Basic accounting rules", body: "Reconciliation is...", author_id: 2, id: 2 },
        { title: "Soccer tournament", body: "Made it to the final...", author_id: 3, id: 3 }
    ],
    authors: [
        { id: 1, name: "Thabo" },
        { id: 2, name: "Thabang" },
        { id: 3, name: "Nkosi" }
    ]
}
Two way relationship between Posts and Authors

To start using the GraphQL API issue the commands below in the root directory of your application:

  • npm i json-graphql-server
  • json-graphql-server db.js --p 3001

The json-graphql-server package gives us a full GraphQL API, so we get both queries and mutations for the entities in the database file.

To use a port other than 3001 or host other than localhost, you can run:

  • json-graphql-server db.js --p <your port here>  
  • json-graphql-server db.js --h <your host here>

To test the API, go to http://localhost:3001/?query={ allPosts { title body Author { name } } }.

Step 2: Get started with  Apollo Client

If you want to build apps with React and GraphQL, Apollo is the library you should use. Apollo is the glue that binds these two tools together. Plus it makes working with React and GraphQL a lot easier by giving us a lot of custom React hooks and features that enable us to both write GraphQL operations and execute them with JavaScript code.

Apollo is a library that brings together two incredibly useful technologies used to build web and mobile apps: React and GraphQL.

Let's install the packages we need and initialize the Apollo Client:

npm install @apollo/client graphql
Run this command in the root directory
  • The @apollo/client package contains virtually everything you need to set up Apollo Client. It includes the in-memory cache, local state management, error handling, and a React-based view layer.
  • The graphql package provides logic for parsing GraphQL queries.

Now that we have the dependencies we need, let's configure an ApolloClient instance inside the index.js file:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

const client = new ApolloClient({
  uri: 'http://localhost:3001/',
  cache: new InMemoryCache()
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);
Connect Apollo Client to React with the Apollo Provider component.

Let's go over the code configuration above:

  • Import the symbols we need from @apollo/client to setup the GraphQL API.
  • Initialize ApolloClient with uri (URL of our GraphQL server ) and cache (cache query results after fetching them) fields.
  • ApolloProvider wraps the React app and places Apollo Client on the context, which enables you to access it from anywhere in your component tree.

After your ApolloProvider is hooked up, you can start requesting data with useQuery which is a React hook that shares GraphQL data with your UI.

Step 3: Fetch data using the GraphQL API

We initially hardcoded application data in the Home.js component. Now we will modify this and allow our component to execute the POSTS_QUERY with the useQuery hook. Let's first define the query by wrapping it in the gql template literal:

import { useQuery, gql } from "@apollo/client";

const POSTS_QUERY = gql`
    query {
        allPosts {
            title
            body
            Author {
                name
            }
        }
    }
`;

const Home = () => {

    const { loading, error, data } = useQuery(POSTS_QUERY);

    return (
        <div className="home">
            {error && <p>Error :(</p>}
            {loading && <p>Loading...</p>}
            {data && Object.values(data).map(posts => (
                posts.map((post, index) => (
                    
                    <div className="blog-preview" key={index}>
                        <h2>{post.title}</h2>
                        <p>Written by {post.Author.name}</p>
                    </div>
                ))
            ))}
        </div>
    );
};

export default Home;
Executing a fetch query

Whenever this component renders, the useQuery hook automatically executes our query and returns a result object containing loading, error, and data properties:

  • Apollo Client tracks a query's error and loading state for you, which are reflected in the loading and error properties.
  • When the result of your query comes back, it's attached to the data property.

Step 4: Add data using the GraphQL API

Now we can focus on adding data to our database, in order to do this we need to look at controlled inputs (forms). Forms lets us track input values from the user, you can have a look at the Create.js component in my GitHub consume-graphql-api repo.

The useMutation React hook is the primary API for executing mutations in an Apollo application.

First, we'll create a corresponding GraphQL ADD_POST mutation. Remember to wrap GraphQL strings in the gql function to parse them into query documents:

import { gql, useMutation } from '@apollo/client';

const ADD_POST = gql`
    mutation AddPost($title: String!, $body: String!, $author_id: ID!) {
        createPost(title: $title, body: $body, author_id: $author_id) {
            Author {
                name
            }
        }
    }
`;
We provide dynamic variables directly to the mutate function

We will capture the blogs' post data using this form:

To execute a mutation, you first call useMutation within a React component and pass it the mutation you want to execute, like so:

const Create = () => {

    const history = useHistory();
    const [title, setTitle] = useState('');
    const [body, setBody] = useState('');
    const [author_id, setAuthor] = useState(1);
    const [addPost, { data, loading, error }] = useMutation(ADD_POST);

    return (
        <div className="create">
            <h2>Post Details</h2>
            <form onSubmit={e => {
                e.preventDefault();
                addPost({ variables: { title, body, author_id } });
                history.push('/'); // got to home page
            }}>
                <label>Post title:</label>
                <input type="text" required value={title}
                    onChange={(e) => setTitle(e.target.value)} />
                <label>Post body:</label>
                <textarea required value={body}
                    onChange={(e) => setBody(e.target.value)}></textarea>
                <label>Post author:</label>
                <select value={author_id}
                    onChange={(e) => setAuthor(e.target.value)}>
                    <option value="1">Thabo</option>
                    <option value="2">Thabang</option>
                    <option value="3">Nkosi</option>
                </select>
                <br/>
                <button>Upload</button>
            </form>
        </div>
    );
};

export default Create;
Capture input from user and send to GraphQL

In this Create.js component, our form's onSubmit handler calls the mutate function (named addPost) that's returned by the useMutation hook. This tells Apollo Client to execute the mutation by sending it to our GraphQL server.

Feel free to test the API on http://localhost:3001/?query=mutation { createPost(title%3A "Mutate Function"%2C body%3A "Add data using the GraphQL API"%2C author_id%3A 1) { Author { name } } }.

Summary

This was just scratching the surface on how to consume a GraphQL API from a React Application. The was so much we did, so checkout consume-graphql-api repo on my GitHub if you come across any issues.