How to use GET request to get all data in React with Axios?

How to use GET request to get all data in React with Axios?

A Comprehensive Guide to Utilizing GET Requests in MERN stack application for Retrieving Complete Data with Axios

Introduction

We will work with Axios, MongoDB, Express.js, React and Node.js

When we are developing web applications, we need to send some requests from the client to the server. There are 5 main HTTP request methods that allow us to perform CRUD operations and are used most of the time: GET, POST, DELETE, PUT, and PATCH.

2 main ways to fetch data in React

  • JavaScript built-in module Fetch API

  • Axios, an external library that makes it easier, is what you will learn today.

Code

Install and import Axios

By running the following command in our project's terminal:

$ npm install axios
$ yarn add axios

then you should import axios at the top of your project

import axios from "axios";

Hooks

We'll use the useEffect hook to fetch all data and the useState hook to store it.

import { useEffect, useState } from "react";

Code inside the functional component should look like this.

export default function Home() {

Making an empty array state with useState hook where we will store the response (data).

const [data, setData] = useState([]);

Calling a function when the page is mounted with the useEffect hook: When you don't specify anything inside square brackets, it will be called when the page is mounted.

    useEffect(() => {
      getAlldata();
    }, []);

When you're waiting for something from the server, use async-await with the async function to fetch the data. If you don't use async-await, other operations may start performing before you get the desired data from the server, leading to unexpected behavior. I didn't understand it until I tried it and faced unexpected behavior for the first time. So, don't be discouraged if you don't grasp it immediately.

We are using try-catch block to catch possible errors (we'll put it in the state and improve it later in the code).

     const getAlldata = async () => {
      try {
        const { res } = await axios.get(
           //specify your BACKEND_URL inside .env file
           //BACKEND_URL=http://localhost:8000 (don't forget to change and
           //put this inside your enviroment variables when deploying)
          `${process.env.BACKEND_URL}/allData`
        );
        setData(res); //Set the fetched data in the state
      } catch (error) {
        console.error(error);
      }
    };

    return (
      <main>
        {/* pass the data to some component or use it inside this component */}
        <Component data={data} />
      </main>
    );
  }

Details

Add loading and error states for a better user experience. When you are waiting for data to fetch, you should show users something.

For the error state, we don't want to just log it to the console. We want to show it to the user as well.

(watch the comments inside code block)

export default function Home() {

    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState("");

    useEffect(() => {
      getAlldata();
    }, []);

    const getAlldata = async () => {
      try {
        //while waiting for the answer you setLoading to true
        setLoading(true);

        const { res } = await axios.get(
          `${process.env.BACKEND_URL}/getAllUsers`
        );
        //because answer came you setError to empty string
        setError("");
        //when answer came you setLoading to false
        setLoading(false);
        setData(res); 
      } catch (error) {
        //because answer came you setError to error from server
        setError(error.response.data.message);
        //if you catched error you setLoading to false and show error
        setLoading(false);
      }
    };

    return (
      <main>
        {/*if error is true show it to the user*/}
        {error && (
            {error}
         )}
        {/*if loading is true show the loading... (or whatever you want)
        to the user*/}
        {loading && (
            loading...
         )}
        <Component users={users} />
      </main>
    );
  }

Backend code

If you're focused on frontend development only, you can stop reading here. Anyway in this blog, I haven't gone into much detail about backend work.

Npm init

Let's create a folder named "backend," and inside it, in the terminal, type:

$ npm init

In the example file I provided, just install all the required packages and copy the code.

To avoid manually refreshing the server every time you make changes to it by using nodemon do this.

$ npm install nodemon
$ yarn add nodemon

And inside "package.json" file do this.

"scripts": {
    "server": "nodemon server.js"
  },

Create server.js

Next, let's initialize and create a server.js file in the project's root directory (backend folder).

Below, I'll provide an example of a "server.js" file used in some of my previous projects.

If you're unsure about where to find or how to create your MongoDB URL, you can refer to my other blog for guidance.

const express = require("express");
const cors = require("cors");
const fileUpload = require("express-fileupload");
const dotenv = require("dotenv");
const mongoose = require("mongoose");
const { readdirSync } = require("fs");
dotenv.config();
const app = express();
app.use(express.json());
app.use(cors());
app.use(fileUpload({
    useTempFiles:true,
}));

//Clarity, so you don't have to put /user in app.use("/user") and 
//useRouter for every new file.
//Instead, every time we create a new one, it will be automatically
//added here in routes.
readdirSync("./routes").map((r) => app.use("/", require("./routes/" + r)));

//database
mongoose
   //you should specify MongoDB URI in your env file
  .connect(process.env.DATABASE_URL, {
    useNewUrlParser: true,
  })
  .then(() => console.log("database connected successfuly"))
  .catch((err) => console.log("error connecting to mongodb", err));

//If it exists it is process.env.PORT, if it doesn't exist,
//the default port is 8000.
const PORT = process.env.PORT || 8000;

app.listen(PORT, () => {
  console.log(`server is running on port ${PORT}...`);
});

Create routes folder

Inside the "backend" folder, create a "routes" folder, and within the "routes" folder, create a file named "user.js."

Here's an example for the "user.js" file in the "routes" folder.

const express = require("express");
const {
  getAllUsers
} = require("../controllers/user");

const router = express.Router();

//home pozivamo funkciju iz controllersa jer funkcije po nekad mogu da budu vrlo zajebane
router.get("/getAllUsers", getAllUsers);

module.exports = router;

If you want to add more endpoints, simply stack them up like this.

const express = require("express");
const {
  getAllUsers,
  findUser
} = require("../controllers/user");

const router = express.Router();

//home pozivamo funkciju iz controllersa jer funkcije po nekad mogu da budu vrlo zajebane
router.get("/getAllUsers", getAllUsers);
router.post("/findUser", findUser);

module.exports = router;

Create controllers folder

Now let's create a "controllers" folder and inside it, a file named "user.js."

const User = require("../models/User");

exports.getAllUsers = async (req, res) => {
  try {
    const users = await User.find()
      //with select you are selecting just what you want from User model
      .select(
        "first_name last_name bYear"
      )
      .sort({ createdAt: -1 });
    res.json(users);
  } catch (error) {
    return res.status(500).json({ message: error.message });
  }
};

Create models folder for MongoDB models

inside it create "User.js" file

const { ObjectId } = require("mongodb");
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema(
  {
    first_name: {
      type: String,
      required: [true, "first name is required"],
    },
    last_name: {
      type: String,
      required: [true, "last name is required"],
    },
    bYear: {
      type: Number,
      required: true,
    },
  },
  {
    //this will automaticaly make you createdAt and updatedAt
    timestamps: true,
  }
);

module.exports = mongoose.model("User", userSchema);

Boilerplate users code for the given MongoDB model

Make "users.json" file in the root of your project (or anywhere else).

[
    {
      "first_name": "John",
      "last_name": "Doe",
      "bYear": 1990
    },
    {
      "first_name": "Jane",
      "last_name": "Smith",
      "bYear": 1985
    },
    {
      "first_name": "Michael",
      "last_name": "Johnson",
      "bYear": 1998
    },
    {
      "first_name": "Emily",
      "last_name": "Brown",
      "bYear": 1992
    }
  ]

Now inside your MongoDB Compass go to the add data -> import JSON or CSV file, and upload users.json

That's it! You have successfully fetched all users from the MongoDB database.