Mastering CRUD Operation with AXIOS

Mastering CRUD Operation with AXIOS

Introduction

In this article, I am going to show you how we can create data, retrieve data from the database as well as delete data from the database, which is the essence of CRUD operation using AXIOS.

We will build a contact list, more like a phonebook but for a computer, we will use a JSON server which will stand as our database for this project to help us practice.

So get ready to learn how to make apps do all these tricks: creating, reading, updating, and deleting data. By the end, you'll be a coding magician, able to make apps work like a charm. Let's dive in and create some coding magic with CRUD and Axios! 🚀

Prerequisite

You should have Node.js installed on your system, you should have intermediate understanding of CSS, Javascript and React.js. The knowledge of Tailwind is not a necessity.

What is CRUD?

CRUD is a word used for codes that interact with a database or API(Application Programming Interface). The word “CRUD” is a computer software terminology for create, read, update and delete functions. Create refers to the process of creating a new document in the database, read is the process of retrieving data or reading data from a database while update is the process of editing or updating data on a database and delete, as the name implies is for removing data from a database.

These functions are the basic operation of a model or application, they are necessary to implement persistent storage on an application. A CRUD operation largely involves two parts; the user interface (UI) which interacts with the database and the database or API itself. One fundamental task of a web application is to communicate with the servers through the HTTP(HyperText Transfer Protocol) protocols. This can be easily achieved through the use of Axios and fetch APIs. For this tutorial, we will focus on AXIOS and not fetch API. So, now let’s understand what AXIOS is.

What is AXIOS?

Axios is a popular JavaScript library that allows web browsers to conveniently make HTTP requests. It simplifies the process of interacting with APIs making it an excellent tool for performing CRUD operations (Create, Read, Update and Delete) within a React application.

Within React, Axios is frequently employed to establish AJAX calls to APIs and carry out CRUD operations in web applications. It is an alternative to the built-in fetch API, even though it uses a similar syntax to the fetch API. AXIOS is easier to work with because it uses JavaScript promises to handle asynchronous requests. Axios can be used both on the server side and the client side.

To build a well-defined application as a developer, oftentimes, you’ll be required to consume APIs for the smooth functioning of your applications, which can either be internal or third-party. And right here is where Axios comes into play. Remember, we said we want to interact with an API using Axios to make our HTTP request. For this tutorial, we want to create a fake REST(Representational State Transfer) API using a JSON file as our source through the JSON server.

What is a JSON Server?

JSON Server is a tool that allows developers to easily create a RESTful API using a JSON file as a data source. It is a quick and easy way to mock up a functional API for prototyping or testing purposes. JSON Server is developed utilizing Node.js and can be installed via “npm”. Other package managers like yarn can also be used to install it.

JSON server is not a production-grade server but is usually used to interact with the web as a prototype of APIs from the backend. Having understood all this, let's see an example of how we can use CRUD operation in our web application by making HTTP requests with AXIOS. We will build a contact list that enables us to create, read, update and delete information.

How to build a CRUD App

You will need to install node.js and npm on your system from the Node JS official website, after you have done that then we can proceed.

Step 1: Install dependencies

i. We will create a react app using create-react-app named “crud”. So, in your terminal type in the following command:

npx create-react-app crud

ii. Install Axios

npm install axios

iii. Install React - Browser - Router for creating multiple routes:

npm install --save react-browser-router

iv. We will use tailwind.css to style our CRUD app, so let’s install it:

npm install -D tailwindcss postcss autoprefixer

v. Create a ‘postcss.config.js’ in your root folder and paste this code in it:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}

vi. Modify your ‘tailwind.config.js’ to look like this:

module.exports = {
content: ["./src/**/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
}

vii. Navigate to your ‘index.css’ file inside your ‘src’ folder and modify it to contain only the following configuration code:

@tailwind base;
@tailwind components;
@tailwind utilities;

viii. Install JSON server

To install JSON server globally on your system you can use the following command:

npm install -g json server

To install it locally for a particular project such as this, we use:

npm install json-server

Step 2: Create a JSON file

In your project directory, we will create a JSON file to act as our data source. In your folder, create a new file and give it any name with a ‘.json’ extension, I have named mine ‘db.json’.

Screenshot_2023_08_27-1.png

Step 3: Create our data

Our JSON data is a javascript object type that contains key-value pairs. For this tutorial, I have created data containing a unique id for each entry, name, email, and phone number. Paste the following into your db.json file:

{
  "users": [
    {
      "name": "Ajuebor Goodness",
      "email": "ajueborgood@gmail.com",
      "phoneNumber": "09033333333",
      "id": 1
    },
    {
      "id": 2,
      "name": "Kingsley Stone",
      "email": "kingstone@gmail.com",
      "phoneNumber": "09033333333"
    },
    {
      "id": 3,
      "name": "Kingsley Stone",
      "email": "kingstone@gmail.com",
      "phoneNumber": "09033333333"
    },
    {
      "id": 4,
      "name": "Kingsley Stone",
      "email": "kingstone@gmail.com",
      "phoneNumber": "09033333333"
    },
    {
      "id": 5,
      "name": "Kingsley Stone",
      "email": "kingstone@gmail.com",
      "phoneNumber": "09033333333"
    }
]
}

Step 4: Start the server

We will run our JSON server on web to To start up our JSON server we will run the following command:

npx json-server --watch

And in our case:

npx json-server --watch db.json

This will run on the port "https://localhost:3000" but to change the port we can use the —port flag.

Our react app also runs on "https://localhost:3000" by default, so we can change the port for our JSON server to "https://localhost:8000" with the following command:

npx json-server --watch db.json --port 8000

Result:

Step 5: Create New Files

Now, create a new folder in your ‘src’ folder called ‘components’, inside the ‘components ‘ folder create four files namely: Create.jsx, Home.jsx, Update.jsx, Read.jsx. We will come back to this.

Step 6: Modifying Our Apps

The first file we will modify is the App.js file. Paste the following code into the file:

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './assets/Home';
import Create from './assets/Create';
import Update from './assets/Update';
import Read from './assets/Read';

function App() {

  return (
  <BrowserRouter>
    <Routes>
      <Route path='/' element={<Home />}></Route>
      <Route path='/create' element={<Create />}></Route>
      <Route path='/update/:id' element={<Update />}></Route>
      <Route path='/read/:id' element={<Read />}></Route>
    </Routes> 
  </BrowserRouter>  
  );
}

export default App;

These lines of code simply provide a route for the application.

Modify Home.jsx

In our newly created file, Home.jsx, paste the following code:

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';

function Home() {
  const [data, setData] = useState([]);

  const handleDelete = (id) => {
    const confirm = window.confirm("Are you sure you want to delete this user?");
      if(confirm){
        // Perform the DELETE request to remove the user data
    axios
    .delete(`http://localhost:8000/users/${id}`)
    .then((res) => {
      // To refresh page
      window.location.reload();
    })
    .catch((err) => console.log(err));
      }

  };

  useEffect(() => {
    axios
      .get('<http://localhost:8000/users>')
      .then((res) => setData(res.data))
      .catch((err) => console.log(err));
  }, []);

  return (
    <div className='flex flex-col items-center justify-center bg-blue-200 gap-5 min-h-screen'>
      <h1 className='font-bold mt-3'>LIST OF USERS</h1>
      <div className='bg-white shadow-lg rounded-md w-4/5 m-auto text-center overflow-x-auto'>
        <button className='flex justify-start ml-4 mt-4 bg-green-400 rounded-md p-1'>
          <Link to='/create'>Add User</Link>
        </button>
        <div className='overflow-x-auto'>
          <table className='w-full'>
            <thead>
              <tr>
                <th className='p-2'>ID</th>
                <th className='p-2'>Name</th>
                <th className='p-2'>Email</th>
                <th className='p-2'>Phone Number</th>
                <th className='p-2'>Actions</th>
              </tr>
            </thead>
            <tbody>
              {data.map((d, i) => (
                <tr key={i}>
                  <td className='p-2'>{d.id}</td>
                  <td className='p-2'>{d.name}</td>
                  <td className='p-2'>{d.email}</td>
                  <td className='p-2'>{d.phoneNumber}</td>
                  <td className='p-2 flex'>
                    <button className='bg-green-300 p-1 rounded'><Link to={`/read/${d.id}`}>Read</Link></button>
                    <button className='bg-blue-500 gap-1 p-1 rounded ml-1 mr-1'><Link to={`/update/${d.id}`}>Edit</Link></button>
                    <button className='bg-red-400 gap-1 rounded p-1' onClick={e => handleDelete(d.id)}>Delete</button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

export default Home;

Now, let’s understand the code above.

First, we installed Axios to perform the CRUD operation, useState hook and useEffect hook to manage state and the perform side effects respectively in our application and Link to navigate to certain pages, into our project.

We then created a function ‘Home’ which represents the home page of our application. Inside the function we create a data variable to store our users and setData to update the list using useState hook.

Next, we create a handleDelete variable that deletes entry based on the id supplied, it confirms if you want to delete the entry and completes the action based on your input and also automatically refreshes the page after a delete action. Axios uses the ‘delete’ method to delete data from an entry. We will use the axios syntax DELETE/users/:id to delete only a specific id.

We then implement the useEffect hook to fetch data from our API(JSON server) and update the data state with the response. This JSX file returns a table with the header; id, Name, Email, Phone Number and for the table body, the user data from the state data is mapped over to provide details for each cell, and for each user a Read, Update, and Delete button is added to perform varying operations, of which we have seen the delete operation and every other operations will be discussed shortly.

The delete operation removes an entry from our server once the action is called.

Modify Create.jsx

The Create operation uses the axios ‘post’ method to send new entries into the JSON server once the action is triggered. Now add the following code to Create.jsx file:

import axios from 'axios';
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';

const Create = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    phoneNumber: '',
  });

  const [formErrors, setFormErrors] = useState({
    name: '',
    email: '',
    phoneNumber: '',
  });

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));

    // Clear the specific error message when the field is changed
    setFormErrors((prevErrors) => ({
      ...prevErrors,
      [name]: '',
    }));
  };

  const navigate = useNavigate();

  const handleSubmit = (event) => {
    event.preventDefault();

    // Validate form fields
    const errors = {};

    if (formData.name.trim().length === 0 || !formData.name.match(/^[A-Za-z]+( [A-Za-z]+)*$/)) {
      errors.name = 'Name must contain only alphabets';
    }

    const emailRegex = /^[A-Za-z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Za-z0-9.-]+$/;
    if (formData.email.trim().length === 0 || !formData.email.match(emailRegex)) {
      errors.email = 'Invalid email address';
    }

    if (formData.phoneNumber.trim().length === 0 || !formData.phoneNumber.match(/^[0-9]+$/)) {
      errors.phoneNumber = 'Phone number must contain only numbers';
    }

    if (Object.keys(errors).length === 0) {
      // Perform the POST request only if there are no validation errors
      axios
        .post('<http://localhost:8000/users>', formData)
        .then(() => navigate('/'))
        .catch((err) => console.log(err));

      // Clear form fields after successful submission
      setFormData({
        name: '',
        email: '',
        phoneNumber: '',
      });

      // Clear all form error messages
      setFormErrors({});
    } else {
      // Display form validation errors
      setFormErrors(errors);
    }
  };

  return (
    <div className="bg-blue-200 min-h-screen flex items-center justify-center">
      <div className="w-full sm:w-3/4 md:w-1/2 lg:w-1/3 bg-white p-6 rounded-lg shadow-md">
        <h2 className="text-xl mb-4">Contact Information</h2>
        <form onSubmit={handleSubmit}>
          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-1" htmlFor="name">
              Name
            </label>
            <input
              type="text"
              name="name"
              id="name"
              className="w-full p-2 border rounded-md"
              value={formData.name}
              onChange={handleChange}
            />
            {formErrors.name && <p className="text-red-500 text-sm">{formErrors.name}</p>}
          </div>

          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-1" htmlFor="email">
              Email
            </label>
            <input
              type="email"
              name="email"
              id="email"
              className="w-full p-2 border rounded-md"
              value={formData.email}
              onChange={handleChange}
            />
            {formErrors.email && <p className="text-red-500 text-sm">{formErrors.email}</p>}
          </div>

          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-1" htmlFor="phone">
              Phone Number
            </label>
            <input
              type="tel"
              name="phoneNumber"
              id="phoneNumber"
              className="w-full p-2 border rounded-md"
              value={formData.phoneNumber}
              onChange={handleChange}
            />
            {formErrors.phoneNumber && (
              <p className="text-red-500 text-sm">{formErrors.phoneNumber}</p>
            )}
          </div>

          <div className="flex justify-between">
            <button type="submit" className="bg-blue-500 text-white py-2 px-4 rounded-md">
              Save
            </button>
            <Link to="/" className="text-blue-500 py-2 px-4">
              Back to Home
            </Link>
          </div>
        </form>
      </div>
    </div>
  );
};

export default Create;

The code above shows how the Create in CRUD operations is implemented using the ‘post’ method on axios. So, we start by importing useState for managing states, axios for fetching data, Link and useNavigate for navigating pages.

We then use the ‘useState’ hook to create a variable called formData to hold the user’s input data for name, email, and phone number. We also create a formErrors variable using the useState hook to handle the form validation for each input.

Next, we create a handleChange variable that updates the formData with the new value and also clears any error message related to the changed field. This variable is called each time a user types in an input field.

Then, we create a navigation function using the ‘useNavigate’ hook to move to different parts of the app where needed.

After that we create a handleSubmit function which is called each time the submit button is clicked. It validates the form by matching each entry with a regex expression and checking if the form is not empty, it then performs a post operation if there are no errors. Axios uses the post method to add new data into the database. So, the axios syntax will be POST/users.

This Create component returns a form that allows you to add a new entry to our JSON server.

Modify Read.jsx

Axios uses the get method to read data, when we read data we are simply retrieving data from the database (JSON server in our case). Let’s paste the following lines of code in Read.jsx file to get it working:

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { Link, useParams } from 'react-router-dom';

function Read() {
  const [data, setData] = useState([]);
  const { id } = useParams();

  useEffect(() => {
    axios
      .get(`http://localhost:8000/users/${id}`)
      .then((res) => setData(res.data))
      .catch((err) => console.log(err));
  }, [id]);

  return (
    <div className="bg-blue-200 min-h-screen flex items-center justify-center">
      <div className="w-full sm:w-3/4 md:w-1/2 lg:w-1/3 bg-white p-6 rounded-lg shadow-md">
        <h2 className="text-xl mb-4">User Details</h2>
        <div className='mb-2'>
          <strong>Name: {data.name}</strong>
        </div>
        <div className='mb-2'>
          <strong>Email: {data.email}</strong>
        </div>
        <div className='mb-2'>
          <strong>Phone Number: {data.phoneNumber}</strong>
        </div>
        <div className="flex justify-between">
            <button type="submit" className="bg-blue-500 text-white py-2 px-4 rounded-md">
              <Link to={`/update/${id}`}>
              Edit
              </Link>
            </button>
            <Link to="/" className="text-blue-500 py-2 px-4">
              Back to Home
            </Link>
          </div>
      </div>
    </div>
  )
}

export default Read;

Now, let’s break down this code;

We create a functional component named Read, we then import axios for fetching our data, useState for managing state, useEffect for handling side effects, Link for navigating the app and useParams to route parameters.

We will use the ‘useState’ hook to manage the data state which holds the user’s details. We are also using the ‘useParams’ hook to extract id parameters from the route.

Using the useEffect hook, we will retrieve the user’s details using the axios ‘get’ method, which updates the data state with the response from the server.

We are retrieving our user’s data based on a specific id hence the need for useParams. The axios syntax for extracting the data from our server will be GET/users/:id.

This Read component returns detailed information about the specific user with the id and buttons to either navigate to the Update page or back to the home page.

Modify Update.jsx

For the Update.jsx, we want to retrieve our user’s information from the server, modify it and post it back to the server. We will use the axios syntax PUT/users/:id.

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';

function Update() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    phoneNumber: '',
  });

  const [formErrors, setFormErrors] = useState({
    name: '',
    email: '',
    phoneNumber: '',
  });

  const { id } = useParams();
  const navigate = useNavigate();

  useEffect(() => {
    axios
      .get(`http://localhost:8000/users/${id}`)
      .then((res) => {
        const userData = res.data;
        setFormData({
          name: userData.name,
          email: userData.email,
          phoneNumber: userData.phoneNumber,
        });
      })
      .catch((err) => console.log(err));
  }, [id]);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));

    // Clear the specific error message when the field is changed
    setFormErrors((prevErrors) => ({
      ...prevErrors,
      [name]: '',
    }));
  };

  const handleSubmit = (event) => {
    event.preventDefault();

    // Validate form fields
    const errors = {};

    if (formData.name.trim().length === 0 || !formData.name.match(/^[A-Za-z]+( [A-Za-z]+)*$/)) {
      errors.name = 'Name must contain only alphabets';
    }

    const emailRegex = /^[A-Za-z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Za-z0-9.-]+$/;
    if (formData.email.trim().length === 0 || !formData.email.match(emailRegex)) {
      errors.email = 'Invalid email address';
    }

    if (formData.phoneNumber.trim().length === 0 || !formData.phoneNumber.match(/^[0-9]+$/)) {
      errors.phoneNumber = 'Phone number must contain only numbers';
    }

    if (Object.keys(errors).length === 0) {
      // Perform the PUT request to update the user data
      axios
        .put(`http://localhost:8000/users/${id}`, formData)
        .then(() => navigate('/'))
        .catch((err) => console.log(err));
    } else {
      // Display form validation errors
      setFormErrors(errors);
    }
  };

  return (
    <div className="bg-blue-200 min-h-screen flex items-center justify-center">
      <div className="w-full sm:w-3/4 md:w-1/2 lg:w-1/3 bg-white p-6 rounded-lg shadow-md">
        <h2 className="text-xl mb-4">Update Information</h2>
        <form onSubmit={handleSubmit}>
          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-1" htmlFor="name">
              Name
            </label>
            <input
              type="text"
              name="name"
              id="name"
              className="w-full p-2 border rounded-md"
              value={formData.name}
              onChange={handleChange}
            />
            {formErrors.name && <p className="text-red-500 text-sm">{formErrors.name}</p>}
          </div>

          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-1" htmlFor="email">
              Email
            </label>
            <input
              type="email"
              name="email"
              id="email"
              className="w-full p-2 border rounded-md"
              value={formData.email}
              onChange={handleChange}
            />
            {formErrors.email && <p className="text-red-500 text-sm">{formErrors.email}</p>}
          </div>

          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-1" htmlFor="phone">
              Phone Number
            </label>
            <input
              type="tel"
              name="phoneNumber"
              id="phoneNumber"
              className="w-full p-2 border rounded-md"
              value={formData.phoneNumber}
              onChange={handleChange}
            />
            {formErrors.phoneNumber && (
              <p className="text-red-500 text-sm">{formErrors.phoneNumber}</p>
         )}
        </div>

        <div className="flex justify-between">
          <button type="submit" className="bg-blue-500 text-white py-2 px-4 rounded-md">
             Update
          </button>
          <Link to="/" className="text-blue-500 py-2 px-4">
              Cancel
          </Link>
        </div>
      </form>
    </div>
    </div>
  )
}

export default Update;

Let’s break down the code.

We create a functional component named Update, we then import axios for fetching our data, useState for managing state, useEffect for handling side effects, Link, useNavigate for navigating the app and useParams to route parameters.

We will use the ‘useState’ hook to manage the formData state which holds the user’s details, and formErrors to validate the form. We are also using the ‘useParams’ hook to extract id parameters from the route and the useNavigate hook to move to any page when necessary.

Then we use the useEffect hook to retrieve all the user’s details from the server. A handleChange variable is created to update the formData with the new value and also clear any error message related to the changed field. This variable is called each time a user types in an input field. In the handleSubmit variable, we validate the entries so that no empty field is submitted and each field is matched with a regex expression, once there is no error the form is submitted and the user’s data is updated by using axios ‘put’ method.

Then the JSX element returns a form with the previous information of the user to be updated and buttons to either cancel and go back to the home page or submit the edited information.

This is what our CRUD app looks like:

Conclusion

And that is it on building a CRUD app with Axios. Congratulations!

In this tutorial, we learned how to perform the full CRUD operation with data from your JSON database. You also learnt how to fetch the data using Axios.

You can find the code for this project on my Github.

If you find this article helpful, kindly share it with other developers who might need it as well.

Good job building this project, remember, your little progress matters!