Back

Feb 17, 2023

Feb 17, 2023

Build a Task Manager CRUD App with React and Hygraph

Let's build a fully functional CRUD application using Hygraph, React, and ApolloClient. The task manager we'll have at the end will help you stay organized!

decorative cover image showing crud task manager
decorative cover image showing crud task manager
decorative cover image showing crud task manager

In this article, we will introduce readers to how to build a fully functional CRUD application using Hygraph as our backend, React as our frontend, and ApolloClient to manage our state and fetch and mutate our data. We’ll leverage the Content and Mutation APIs that the Hygraph software exposes to us to perform a simple example of querying and mutating data using a task manager app: creating tasks, reading tasks, updating tasks, and finally deleting a task with Apollo Client. We’ll also demonstrate how to set up and manage our content and connect Hygraph to React.

What is CRUD?

CRUD is an abbreviation for Create, Read, Update, and Delete. These are the four basic operations that a software application should be able to perform. These applications allow users to generate data, read data from the UI, update data, and delete data.

CRUD apps consist of three components in fully fledged applications:

  • API (or server): the code and procedures.

  • Database: stores information and makes it accessible to users.

  • User interface (UI): makes it easier for users to use the application.

When using Restful APIs to perform CRUD operations and making API requests, GET, POST, PUT, and DELETE are the most commonly used HTTP methods. GraphQL uses two types of API requests: Queries and Mutations. A query is used to read the data while mutation is used to create, update and delete the data, which we’ll do once we’ve built our task manager.

Why use Hygraph for our Task Manager?

Hygraph, formerly known as GraphCMS, is a backend-only content management system (i.e., a headless CMS) that uses GraphQL to query data and perform mutations (or updates) to the content, making it accessible via a single endpoint (API) for display on any device without a built-in frontend or presentation layer. It allows teams to use a single content repository to deliver content from a single source to endless frontend platforms via API, such as websites, mobile apps, TVs, and so on. Hygraph also allows teams to use any preferred tech stack or framework, including popular ones like React, Angular, or Vue. It integrates easily with Netlify, Vercel, and Gatsby Cloud for quick previews.

To follow this tutorial, you need:

Building the Backend Data Structure in Hygraph

Before you have access to the Hygraph admin panel, you’ll need to create an account (if you don’t have one already). Hygraph is simple and user-friendly, providing you with an intuitive UI and a good user experience.

Once you’ve signed up, create a project name, choose the region where you want your data to be stored and served, and choose a plan. For our task manager project, we are using a free forever plan.

From our admin dashboard, on the left, below environments, click on Schema. This will allow us to create a model. We have named our content type Task and added fields to our content.

Based on the image above, we created three fields:

  • title - (single-line text) - The title of the task.

  • description - (Multi-line text) - The description of the task.

  • assigned to - (Multi-line text) - Who the task is assigned to.

Adding Content

Even though we can create tasks from the front end of our task manager app, we can also create new tasks from your Hygraph Admin Dashboard. Click on Content, then Create entry. Fill out the information.

Once you have filled out the available fields, click on the save and publish button. You can go back and create more items.

Hygraph API Playground

Hygraph provides us with a GraphQL API playground where we can test our queries and perform mutation queries.

You can play around with this Hygraph API environment to see the data you are querying.

Setting up Roles and Permissions

Before we begin to query this data inside our React App, we must first get and set up our API endpoint and permissions to open or query any published content.

Go to Project settings > Permanent Auth Tokens > add token, input the name of your token, and click on add & configure permission. Then click on Add permission to add permissions. With this, anybody can make a public API request without requiring authentication.

Store your token somewhere secure. Later on, we’ll use it to authenticate and fetch data from our React task manager app.

Building our Task Manager Frontend with React

In this section, we will install React and other dependencies that we will use to build our app. In your terminal, run either of the following commands:

#yarnyarn create react-app project-manager && cd project-manager

Or

#npmnpx create-react-app project-manager && cd project-manager

To make the development of our application easier, we will use Material-UI. This is a React UI library that adheres to Google's Material Design and offers React components right out of the box to develop our UI. In addition to the Material UI, you will need to use some libraries to connect to our backend (Hygraph):

  • graphql - this package provides logic for parsing GraphQL queries. It is used for interpreting GraphQL queries and mutations.

  • apollo-client - this is a tool that helps connect to our Hygraph GraphQL server. It’s also used to fetch, cache, and modify application data, while automatically updating your UI.

  • react-router-dom - a library that makes it possible to integrate dynamic routing into web applications. It enables you to show pages and lets users navigate through them.

Run the command below to install react-router-dom into your React app.

yarn add react-router-dom

After installing react-router-dom, you need to make it available anywhere in your task manager app. To do this, you need to import BrowserRouter from react-router-dom and then wrap the root (App) component in your index.js file.

root.render(          );

To install Material UI, run the command below in your terminal:

yarn add @mui/material @emotion/react @emotion/styled @mui/icons-material

Connecting Hygraph to React using Apollo Client

Install Apollo Client into our project by running the command below in your terminal:

yarn add @apollo/client graphql

To query tasks from our Hygraph endpoint, we need to develop a GraphQL client that will make our query available across our app. This is something we can do right in the index.js file:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom"
import App from './App';
import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
  uri: process.env.REACT_APP_GCMS_API,
  cache: new InMemoryCache(),
  headers: {
      Authorization: `Bearer ${process.env.REACT_APP_GCMS_AUTH}`,
    },
});
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);

Here we implement the Apollo client and inject it into the application by wrapping it with the Apollo provider.

Create a file in the project’s root directory named .env. Add the following to .env:

REACT_APP_HYG_API= your api keyREACT_APP_HYG_AUTH= your api token

Setting up Routes in our Task Manager Backend

In our src folder, create a new folder called component. In this components folder, we’ll create the following files:

  • Tasks.js - a template component for displaying a single task entry. We will also perform the delete and update operation on the file.

  • TaskList.js - This is a page with a list of all task data.

  • BottomNav.js - for navigating throughout the app.

  • AddTasks.js - a page with a form for adding new tasks.

In your BottomNav.js file, add the code below:

import { AddTask, Task } from '@mui/icons-material';
import { BottomNavigation, BottomNavigationAction } from '@mui/material';
import React, { useState } from 'react'
const BottomNav = () => {
  const [value, setValue] = useState('task');
  return (
                    showLabels            sx={{bgcolor: '#292f38'}}            value={value}            onChange={(event, newValue) => {              setValue(newValue);            }}        >          } />          } />          
  )
}
export default BottomNav

We use Material UI to create navigation in our task manager that allows us to navigate through the list of tasks as well as creating a task.

import React from 'react';
import { Route, Routes } from "react-router-dom";
import AddTask from './components/AddTask';
import TaskList from './components/TaskList';
import BottomNav from './components/BottomNav';
import './App.css'
function App() {
  return (
                              Task Manager                                              } />               } />                                              
  );
}
export default App;

Here, we used features from the react-router-dom library to define our routes and their paths and attach them to their respective components.

We have added additional styles to our application. Update your App.css with the code below:

* {
  margin: 0;
  padding: 0;
}
.container {
  background: linear-gradient(100deg, rgb(182, 40, 111) 50%, #ac2066 0);
  width: 100%;
  padding: 20px;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
.app-wrapper {
  background-color: #292f38;
  width: 30%;
  min-width: 800px;
  height: 600px;
  padding: 30px;
  box-sizing: border-box;
  border-radius: 5px;
  box-shadow: 0 15px 30px rgba(0, 0, 0, 0.4);
}
.header h1 {
  color: #ccc;
  font-weight: 300;
  text-align: center;
  margin: 50px 20px 60px 20px;
  font-family: 'Josefin Slab', serif;
}
.main {
  display: flex;
  flex-direction: column;
  align-items: space-between;
  margin-bottom: 50px;
  width: 100%;
}
.list {
  width: 90%;
  margin: auto;
  max-height: 300px;
  overflow: hidden;
  overflow-y: auto;
}
/* width */
::-webkit-scrollbar {
  width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
  border-radius: 10px;
  background-color: #aaa;
}
/* Handle */
::-webkit-scrollbar-thumb {
  background: #444;
  border-radius: 10px;
}
.list-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 20px 0;
  padding-bottom: 5px;
}
.list-item span {
  color: #999;
}
.list-item h2 {
  color: #999;
}
.no-tasks {
  color: #777;
  text-align: center;
  font-size: 18px;
  margin-top: 20px;
}

Querying Content and Displaying Tasks in our Task Manager

Let’s start by creating a file that we’ll use to store all of our queries and mutation queries. The goal is to migrate all of our code with a simple copy of the file. By doing so, we can manage our API-specific interactions in a single file, edit and update code, and reuse it between files.

In the src folder, create a lib/api.js file and add the following code:

import { gql } from '@apollo/client';
// getting all tasks
export const ALL_TASK = gql`
    query {
        tasks(stage: DRAFT) {
            id
            title
            description
            assignedTo
        }
    }
`

We used gql, a function for passing queries from the Apollo client library that we imported from the Apollo client, to wrap and define the query we want to execute. In our query, we passed a variable DRAFT because we also want to fetch the task in the draft. We are doing this because we won’t perform the published task operation from our front end in this tutorial.

Publishing tasks from the front end without going to the Hygraph dashboard is possible. Read the Hygraph documentation to learn how.

In your components/Tasks.js file, add the following code:

import React  from 'react'
import {AssignmentInd, Delete, Description, Update} from '@mui/icons-material';
import { List, ListItemButton, ListItemIcon, ListItemText, Typography, Stack, Button, Modal, Box, FormControl, OutlinedInput, InputLabel } from '@mui/material';
const Task = ({ task, getTask }) => {
  return (
        
        >
            {task.title}
                
                sx={{bgcolor: '#292f38', color: '#ccc'}}
                />
                
                sx={{bgcolor: '#292f38', color: '#ccc'}}
                />
  )
}
export default Task

We used Material UI to build out our front end where we display the title, description, and assignee of each task. We pass task and getTask as props.

In your component/TaskList.js file, add the following code:

import React from 'react'
import { useQuery } from '@apollo/client';
import { ALL_TASK } from '../lib/api';
import Task from './Task';
import { Typography } from '@mui/material';
const TaskList = () => {
  const { loading, error, data } = useQuery(ALL_TASK)
  if (loading) return 
Getting tasks...
;  if (error) return 
An error occurred
;
  return (
                    Total Tasks: {data.tasks.length}                {data.tasks.length ?            (                                    {data.tasks.map((task) => (                                            ))}                            )            :            (                No Tasks            )        }    
  )
}
export default TaskList;

We imported the useQuery hook provided by the Apollo client and we passed the ALL_TASK GraphQL query to it. We defined three states for the data in our hook.

  • loading - this is helpful while the query is being processed.

  • error - when the query was unsuccessfully processed.

  • data - this contains data returned by Hygrapyh.

Inside the data object, we now have access to the tasks. When the application renders the component, the useQuery hook will be called.

Creating New Tasks with Mutation

To create a task in your task manager, in your lib/api.js file, add the query below:

export const CREATE_TASK =  gql`
    mutation CreateTask($assignedTo: String, $description: String, $title: String) {
        createTask(
            data: {assignedTo: $assignedTo, description: $description, title: $title}
          ) {
            id
            title
            description
            assignedTo
        }
    }
`

Before now, we’ve been calling the backend for data using queries. We now want to play around with them a bit, but to do so, we need to employ mutations. To perform the CRUD operations create, update, and delete, we used mutation as an operation type. We export the CREATE_TASK query; when we build a new model, Hygraph automatically provides a custom function called create__ for us. The name of the model to be created is always prefixed to it. The createTask function was necessary because our model was given the name Task. Use createTask as an operation name and pass on our variables that are required by the backend.

In your AddTask.js file, add the code below:

import React, { useState } from 'react'
import { useMutation } from '@apollo/client'
import { CREATE_TASK } from '../lib/api';
import {OutlinedInput, FormControl, InputLabel, Button, Box} from '@mui/material'
const AddTask = () => {
  const [task, setTask] = useState({});
  const [createTask, { isadding }] = useMutation(CREATE_TASK)
  if (isadding) return 'Submitting...';
  const handleOnChange = (event)=> {
    setTask({ ...task, [event.target.name]: event.target.value});
  }
  const onClick = () => {
    createTask({variables: { ...task }});
  }
  return (
    
    sx={{ maxWidth: '100%'}}>
Title
        
          onChange={handleOnChange}
          name='title'
          label="title"
          sx={{border: '1px solid #cccc'}}
        />
Description
         
          onChange={handleOnChange}
          name='description'
          label="description"
          sx={{border: '1px solid #cccc'}}
        />
Assigned To
        
          onChange={handleOnChange}
          name='assignedTo'
          label="Assigned To"
          sx={{border: '1px solid #cccc'}}
        />
Add Task    
  )
}
export default AddTask

First, we used the useState hook from React to store the provided state. We then used the useMutation hook from the Apollo client. The useMutation hook depends on the createTask function to execute the CREATE_TASK mutation query. If the createTask function gets called, the mutation gets executed. We then use the handleOnChange function to update the state whenever the user inputs data.

After entering dummy content and clicking on Add task in our task manager, if all goes well, a new task will be created and you will be able to view your content in the Hygraph Dashboard. Newly created tasks don’t automatically get published unless we call the publish function or publish it from the dashboard. But, we already defined our query to also fetch content from the draft, so newly created content will automatically be displayed on our front end.

Updating Tasks in our Task Manager

It's easy to update posts by simply adding new content to existing content and updating the Hygraph store. In our lib/api.js file, add the query below:

export const UPDATE_TASk = gql`
    mutation UpdateTask($assignedTo: String, $description: String, $title: String, $id: ID){
        updateTask(
            where: {id: $id}
            data: {assignedTo: $assignedTo, description: $description, title: $title}
        ) {
            assignedTo
            description
            title
        }
}
`

Updating entities using mutations is similar to creating new ones, except you need two arguments in your query: the where object referencing the id of the task you want to update and the data object that holds the data to replace the old content.

We'll need a form to collect the data we'll need to update a task. We’ll create a modal so that when a user tries to update a task by clicking on the update icon, it will call up a modal containing the form.

const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const [replacementTask, setReplacementTask] = useState({});
const handleOnChange = (event)=> {
    setReplacementTask({ ...replacementTask, [event.target.name]: event.target.value });
}
const [updateTask] = useMutation(UPDATE_TASk, {
    refetchQueries: [
        { query: getTask },
    ]
});
const handleUpdate = () => {
    updateTask({ variables: { id: task.id, ...replacementTask }})
}

To better control the process of creating and updating content, we define several states. We then define functions that are responsible for handling form input and calling the Hygraph API services in our query.

Add the handleOpen function to the Update icon:

    sx={{bgcolor: '#292f38', color: '#ccc'}}
    onClick={handleOpen}

With this, when you click on the update button a modal will pop up containing the form field. Now below the Button container, add the modal component from Material UI to build our form field.

      aria-describedby="modal-modal-description">
Title
        
            onChange={handleOnChange}
            name='title'
            label="Title"
            sx={{color: '#cccc'}}
        />
Description
        
            onChange={handleOnChange}
            name='description'
            label="Description"
            sx={{color: '#cccc'}}
         />
Assigned To
        
            onChange={handleOnChange}
            name='assignedTo'
            label="Assigned To"
            sx={{color: '#cccc'}}
        />
      onClick={handleUpdate}     type='submit'>Update

With this, we can now successfully update our task. You will have to republish the task after editing it from the Hygraph dashboard.

Deleting Tasks

In this section, we’ll work on deleting each task from the front end of our task manager. In our lib/api.js file, add the following code:

export const DELETE_TASK = gql`
    mutation DeleteTask($id: ID!) {
        deleteTask(where: {id: $id}) {
            id
            title
            description
            assignedTo
        }
    }
`

Here, we define the DELETE_TASK that we used to delete our task, and referenced the id variable of the task to be deleted.

Update your component/Task.js file with the following code:

const [deleteTask] = useMutation(DELETE_TASK, {
    refetchQueries: [
        { query: getTask },
    ]
})
const handleDelete = () => {
    deleteTask({ variables: { id: task.id }});
}

We also added the property refetchQueries supplied by the useMutation hook to re-fetch our data to reflect the modifications brought on by deleting a task. We called the deleteTask function with the handleDelete function and passed the data of the id to the variable we defined.

    sx={{bgcolor: '#292f38', color: '#ccc'}}
      onClick={handleDelete}

Conclusion

In this tutorial, we learned about the Hygraph headless CMS and how to use Hygraph to create a model, manage content, and set up roles and permissions. Using Hygraph, Apollo Client, React Router Dom, Material UI, and React, we were able to develop a fully functional CRUD task manager application.

You can find the source code for this article on GitHub.

Written by

Written by

Jude Miracle

Jude Miracle

SHARE

SHARE

Title

Title

our newsletter

Sign up for The Pieces Post

Check out our monthly newsletter for curated tips & tricks, product updates, industry insights and more.

our newsletter

Sign up for The Pieces Post

Check out our monthly newsletter for curated tips & tricks, product updates, industry insights and more.

our newsletter

Sign up for The Pieces Post

Check out our monthly newsletter for curated tips & tricks, product updates, industry insights and more.