Build a Simple CRUD App using React and Node

react and node

Table of Contents

In this article, we will learn how to build a simple CRUD app with React.js, Node.js, and TailwindCSS. 

What Is a CRUD App?

A CRUD App performs four basic app operations, namely Create, Read, Update and Delete. Many apps you interact with daily at the core perform these basic tasks. Take Instagram, for example. You can Create profiles and posts stored in the database, Read your and other users’ profiles and posts. These details are retrieved from the stored created data. You can also Update and Delete your posts, profile pictures, etc. 

You may also be interested in: “PayPal Checkout Integration with React”

Apart from requiring a Front-End and Server, these functions require a database like MongoDB to store, retrieve, update and delete user-submitted data. As you can imagine, that takes a lot of effort to set up, which is why I was pleased when I discovered a simple fake REST API we can use to perform basic CRUD tasks for our app.

Goal

Our goal today is to learn the following tasks:

  • Install basic packages using Node.js
  • Create an excellent User interface using React and Tailwind CSS
  • Perform basic CRUD operations with our app.

Prerequisites

  • Knowledge of React js and React Hooks.
  • React 16.8 or later

Create A React App

Let’s get started by using the React CRA boilerplate to create our React App. Since we will be using Tailwind CSS, we will follow the Official Documentation steps to make installation seamless.

Start with,

npx create-react-app my-react-crudapp
cd my-react-crudapp

Open the folder in your favorite IDE and complete the rest of the steps. Next, we need to install React Router DOM – a routing functionality that helps us navigate our app.

npm install react-router-dom

Run, npm run start, and you should get the popular Welcome React Page as shown below. 

React App

First, we will be creating our Navigation component. To do this, we will use components from the Tailwind documentation page as much as possible.

Create a Naviagte.js component in your src folder.

import React from "react";
export default function Navigate(){
   return (
       <nav class="flex items-center justify-between flex-wrap bg-green-500 p-6">
           <div class="flex items-center flex-shrink-0 text-white mr-6">
               <span class="font-semibold text-xl tracking-tight">REACT CRUD APP</span>
           </div>          
           <div>
               <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-green-500 hover:bg-white mt-4 lg:mt-0">
                   CREATE
               </button>
           </div>
       </nav>
   )
}

Next, we will call the react-router-dom and our Navigate.js in the main App.js Component. 

import { Link } from "react-router-dom";
export default function Navigate(){
   return (
       <nav class="flex items-center justify-between flex-wrap bg-green-500 p-6">
       <div class="flex items-center flex-shrink-0 text-white mr-6">
           <span class="font-semibold text-xl tracking-tight">REACT CRUD APP</span>
       </div>
       <Link to="/">
           <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-green-500 hover:bg-white mt-4 lg:mt-0">
               HOME
           </button>
       </Link>
       </nav>
   )
}
React CRUD App

CRUD OPERATIONS

It’s time to start building our CRUD functionalities. Here are the API calls we will be making to achieve this. 

React CRUD App API

First, let create a UsersList.js that will help us read the existing users already provided by the API for testing. Again, we will be an official UI component to build this

UsersList.js

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
 
function UsersList() {
   const [users, setUsers] = useState([]);
   useEffect(() => {
       ReadUsers()
   }, [])
 
   const ReadUsers = () => {
       fetch("https://www.mecallapi.com/api/users")
         .then(res => res.json())
         .then(
           (result) => {
             setUsers(result)
           }
         )
   }
   return (
       <div class="flex flex-col">
           <div class="-my-2 overflow-x-auto">
               <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
                   <div class="lg:flex lg:items-center lg:justify-between px-20 py-2">
                       <div class="flex-1 min-w-0">
                           <h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
                           Users List
                           </h2>
                       </div>
                       <div class="mt-5 flex lg:mt-0 lg:ml-4">
                           <span class="hidden sm:block">
                           <Link to="/create">
                               <button type="button" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                                   <svg class="-ml-1 mr-2 h-5 w-5 text-gray-500"  viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
                                   <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
                                   </svg>
                                   CREATE
                               </button>
                           </Link>
                           </span>
                       </div>
                   </div>
               <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
                   <table class="min-w-full divide-y divide-gray-200">
                   <thead class="bg-gray-50">
                       <tr>
                       <th scope="col" class="px-10 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                           ID
                       </th>
                       <th scope="col" class="px-10 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                           Full Name
                       </th>
                       <th scope="col" class="px-6 py-1 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                           Email Address
                       </th>
                      
                       <th scope="col" class="relative px-6 py-1">
                           <span class="sr-only">Edit</span>
                       </th>
                       </tr>
                   </thead>
                   <tbody class="bg-white divide-y divide-gray-200">
                   {users.map((user) => (
                       <tr key={user.ID}>
                           <td class="px-10 py-4 whitespace-nowrap">
                               <div class="text-sm text-gray-500">
                                   {user.id}
                               </div>
                           </td>
                           <td class="px-6 py-4 whitespace-nowrap">
                               <div class="flex items-center">
                               <div class="flex-shrink-0 h-10 w-10">
                                   <img class="h-10 w-10 rounded-full" src={user.avatar} alt=" />
                               </div>
                               <div class="ml-4">
                                   <div class="text-sm font-medium text-gray-900">
                                  <span> {user.fname}</span> <span>{user.lname}</span>
                                   </div>
                               </div>
                               </div>
                           </td>
                           <td class="px-6 py-4 whitespace-nowrap">
                               <div class="text-sm text-gray-500">
                                   {user.username}
                               </div>
                           </td>
                           <td class="px-6 py-4 space-x-2 whitespace-nowrap text-right text-sm font-medium">
                               <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-blue-800 border-blue-600 hover:bg-blue-300 hover:text-blue-500 mt-4 lg:mt-0">EDIT</button>
                               <button class="inline-block text-sm px-4 py-2 leading-none border rounded text-red-800 border-red-600 hover:bg-red-300 hover:text-red-500 mt-4 lg:mt-0">DELETE</button>
                           </td>
                       </tr>
                       ))}
                   </tbody>
                   </table>
               </div>
               </div>
           </div>
       </div>
   )
}
 
export default UsersList;

In the code block above, we used React useEffect Hook to fetch the list of users once upon load. After that, we stored the received data using a useState Hook and mapped it to the table components provided by Tailwind CSS. In subsequent sections, we will be using the Edit and Delete and Create Button we created.

React CRUD App

CreateUser.js

Next, we will create CreateUser.js that will allow us to create a user. 

import React, { useState } from "react";
 
function CreateUser() {
  
   const handleSubmit = event => {
     event.preventDefault();
     var data = {
       'fname': fname,
       'lname': lname,
       'username': username,
       'email': email,
       'avatar': avatar,
     }
 
     fetch('https://www.mecallapi.com/api/users/create', {
       method: 'POST',
       headers: {
         Accept: 'application/form-data',
         'Content-Type': 'application/json',
       },
       body: JSON.stringify(data),
     })
     .then(res => res.json())
     .then(
       (result) => {
         alert(result['message'])
         if (result['status'] === 'ok') {
           window.location.href = '/';
         }
       }
     )
   }
    const [fname, setFname] = useState('');
   const [lname, setLname] = useState('');
   const [username, setUsername] = useState('');
   const [email, setEmail] = useState('');
   const [avatar, setAvatar] = useState('');
 
   return (
       <form class="w-full max-w-lg mx-auto my-20" onSubmit={handleSubmit}>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-3 md:mb-0">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
               First Name
           </label>
           <input
           class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
           type="text"
           placeholder="First Name"
           onChange={(e) => setFname(e.target.value)}
           label="First Name"
           />
              
          
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
           for="grid-last-name">
               Last Name
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           type="text" placeholder="Last Name"
           onChange={(e) => setLname(e.target.value)}
           label="Last Name"
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
               <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
                   Email Address
               </label>
               <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
               type="email"  placeholder="Email address"
               onChange={(e) => setEmail(e.target.value)}
               label="Email"
               />
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-last-name">
               Username
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           label="Username"
           onChange={(e) => setUsername(e.target.value)}
           type="text" placeholder="janeDoe"
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-1">
           <div class="w-full px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-image">
               Avatar
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-image" type="text"
           placeholder="Image link"
           label="Avatar"
           onChange={(e) => setAvatar(e.target.value)}
           />
           </div>
       </div>
       <button type="submit" class="inline-flex items-center ml-8 px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
           SUBMIT
       </button>
      
       </form>
   )
}
 
export default CreateUser;

In the code block above, we created a form and appended its input values to corresponding state variables using the UseState Hook. We then sent these values to the CRUD REST API to create a user. 

We will also import and use the newly created Component into our App.js:

import CreateUser from './CreateUser'
………………….
<Route path='/create' element={<CreateUser />} />
Create page

UpdateUser.js

The next step is to create a UpdateUser.js component that will allow us successfully edit the created users.

First, we will create a updateUser function in UsersLists.js

const UpdateUser = id => {
   window.location = '/update/'+id
 }

and pass it to the edit button.

onClick={() => UpdateUser(user.id)}

Now, we can create our UpdateUser.js

import React, { useState, useEffect } from "react";
import { useParams } from 'react-router-dom';
 
function UpdateUser() {
    const { id } = useParams();
 
   useEffect(() => {
     fetch("https://www.mecallapi.com/api/users/"+id)
       .then(res => res.json())
       .then(
         (result) => {
           setFname(result.user.fname)
           setLname(result.user.lname)
           setUsername(result.user.username)
           setEmail(result.user.email)
           setAvatar(result.user.avatar)
         }
       )
   }, [id])
    const handleSubmit = event => {
     event.preventDefault();
     var data = {
       'id': id,
       'fname': fname,
       'lname': lname,
       'username': username,
       'email': email,
       'avatar': avatar,
     }
     fetch('https://www.mecallapi.com/api/users/update', {
       method: 'PUT',
       headers: {
         Accept: 'application/form-data',
         'Content-Type': 'application/json',
       },
       body: JSON.stringify(data),
     })
     .then(res => res.json())
     .then(
       (result) => {
         alert(result['message'])
         if (result['status'] === 'ok') {
           window.location.href = '/';
         }
       }
     )
   }
    const [fname, setFname] = useState('');
   const [lname, setLname] = useState('');
   const [username, setUsername] = useState('');
   const [email, setEmail] = useState('');
   const [avatar, setAvatar] = useState('');
    return (
       <form class="w-full max-w-lg mx-auto my-20" onSubmit={handleSubmit}>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-3 md:mb-0">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
               First Name
           </label>
           <input
           class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
           type="text"
           value={fname}
           onChange={(e) => setFname(e.target.value)}
           label="First Name"
           />
              
          
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
           for="grid-last-name">
               Last Name
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           type="text" value={lname}
           onChange={(e) => setLname(e.target.value)}
           label="Last Name"
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-2">
           <div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
               <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-first-name">
                   Email Address
               </label>
               <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name"
               type="email"  value={email}
               onChange={(e) => setEmail(e.target.value)}
               label="Email"
               />
           </div>
           <div class="w-full md:w-1/2 px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-last-name">
               Username
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-last-name"
           label="Username"
           onChange={(e) => setUsername(e.target.value)}
           type="text" value={username}
           />
           </div>
       </div>
       <div class="flex flex-wrap mx-3 mb-1">
           <div class="w-full px-3">
           <label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-image">
               Avatar
           </label>
           <input class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-image" type="text"
           value={avatar}
           label="Avatar"
           onChange={(e) => setAvatar(e.target.value)}
           />
           </div>
       </div>
       <button type="submit" class="inline-flex items-center ml-8 px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
           Update
       </button>
      
       </form>
   )
}
 
export default UpdateUser;

In the code snippet above, we retrieved and displayed the current user ID of the user we would like to edit its details. Next, we updated the values using the form inputs made by the user.

We will also import and use the newly created Component into our App.js:

import UpdateUser from './UpdateUser'
………
<Route path='/update/:id' element={<UpdateUser />}/>
Update Page

DELETE

Lastly, we will be activating the delete button. This doesn’t require a new component instead, we will be updating our UsersList.js Component. 

const DeleteUser = id => {
       var data = {
         'id': id
       }
       fetch('https://www.mecallapi.com/api/users/delete', {
         method: 'DELETE',
         headers: {
           Accept: 'application/form-data',
           'Content-Type': 'application/json',
         },
         body: JSON.stringify(data),
       })
       .then(res => res.json())
       .then(
         (result) => {
           alert(result['message'])
           if (result['status'] === 'ok') {
               ReadUsers()
           }
         }
       )
     }

<button onClick={() => DeleteUser(user.id)} class="inline-block text-sm px-4 py-2 leading-none border rounded text-red-800 border-red-600 hover:bg-red-300 hover:text-red-500 mt-4 lg:mt-0">DELETE</button>

Demo

Summary

That’s all for now. In conclusion, we learn how to use Node.js to install essential packages for our React. Next, we used React Hooks and React Router to create and navigate our components. Lastly, we made basic CRUD interactions to create, read, update and delete user details. 

If you’re interested in further developing your React and Node js skills, here are two tutorials of ours that should help. A Quick Introduction to React Hooks and Stripe Checkout Integration with React.

Unimedia Technology

Here at Unimedia Technology we have a team of Web Developers that can help you develop your most complex React Applications.

Remember that at Unimedia, we are experts in emerging technologies, so feel free to contact us if you need advice or services. We’ll be happy to assist you.

Unimedia Technology

Your software development partner

We are a cutting-edge technology consultancy specialising in custom software architecture and development.

Our Services

Sign up for our updates

Stay updated, stay informed, and let’s shape the future of tech together!

Related Reads

Dive Deeper with These Articles

Explore more of Unimedia’s expert insights and in-depth analyses in the realm of software development and technology.

Let’s make your vision a reality!

Simply fill out this form to begin your journey towards innovation and efficiency.