# From Concept to Completion: Building a Full-Stack App with Image Functionality

# Intro

In this blog, we are going to create a full-stack web application that allows users to sign up, log in, and save images on the database. For this web application, we are going to use ReactJS for frontend, NodeJS, and ExpressJS for the backend server, Cloudinary for uploading images, and MongoDB for saving user details and image links.

So, what are we waiting for? Let's go...

## 1\. Creating the BackEnd

Here we are going to create the backend server. First name your project, I am going to name it as `image-app` , you can name it of your choice.

In the `image-app`, create a `backend` folder and open it in VS Code( or any other editor).

Open the terminal and run the command:-

`npm init -y`

It will create a `package.json` file. Now, to create a backend server, we are going to install some packages:-

* `Express JS` for creating RESTful APIs.
    
* `Mongoose` for simplifying interactions with the MongoDB database.
    
* `Zod` for data validation.
    
* `Cors` that allows a web page to share resources across different origins.
    

Before proceeding to the other things, get a connection string that helps to connect the database with the backend, to get a connection string log on to [https://www.mongodb.com/](https://www.mongodb.com/), then go to Create a new Project, and go to the overview tab, click on Create as shown in the figure.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708429470087/560f2a96-df07-4fde-b55f-85299f441a48.png align="center")

After finishing, you will be directed to the home page. Click on the connect button and get the connection string from there.

The connection string would look like this:-

`mongodb+srv://<user>:<password>@`[`something.something.mongodb.net/`](http://cluster0.oiu1jgx.mongodb.net/)

1. **Connecting the Database to the backend server**
    
    Create a folder named `db` in the `backend` folder, and create `index.js` file.
    
    In this file, we are going to connect with the MongoDB database using Mongoose and also create schemas for User and Image.
    

```javascript
const mongoose = require("mongoose");
mongoose.connect(
  "mongodb+srv://<user>:<password>@something.something.mongodb.net/ "
);

const UserSchema = new mongoose.Schema({
  firstName: {
    type: String,
    required: true,
    trim: true,
    maxLength: 15,
  },
  lastName: {
    type: String,
    require: true,
    trim: true,
    maxLength: 15,
  },
  password: {
    type: String,
    required: true,
    minLength: 8,
  },
  email: {
    type: String,
    unique: true,
    required: true,
  },
});

const ImageSchema = new mongoose.Schema({
  user: {
    ref: "User",
    type: mongoose.Schema.Types.ObjectId,
    required: true,
  },
  imageName: {
    type: String,
    required: true,
  },
  imageLink: {
    type: String,
    required: true,
  },
  cloudinary_id: {
    type: String,
    required: true,
  },
});

const User = mongoose.model("User", UserSchema);
const Image = mongoose.model("Image", ImageSchema);

module.exports = { User, Image };
```

Now you are going to ask why we have cloudinary\_id and imageLink field in the image schema, it is because when we upload images to the Cloudinary, it returns a unique ID, and public link for the image to get access.

We have created schemas for the user and image. It's now time to create routes for them. But before that, it's a good practice to start the server and for that, we have to create a `index.js` file in the `backend` folder.

```javascript
const express = require("express");
const app = express();
const rootRouter = require("./routes/index");
const cors = require("cors");

app.use(cors());
// to parse JSON data sent in the request body
app.use(express.json());
app.use(
  express.urlencoded({
    extended: true,
  })
);

app.use("/api/v1", rootRouter);

app.listen(3000, (req, res) => {
  console.log("Server is running");
});
```

we have used `app.use("/api/v1",rootRouter)` in the index.js file. This is where we are going to define our routes.

1. **Creating Routes for the User and Image**
    

In the `backend` folder, create a folder named `routes` and create `index.js` , `user.js` , and `image.js` files.

`index.js` file - This is where routes will be divided for user and image.

```javascript
const { Router } = require("express");
const router = Router();
const UserRoute = require("./user");
const ImageRoute = require("./image");

router.use("/user", UserRoute);
router.use("/image", ImageRoute);

module.exports = router;
```

`user.js` file - This is where we are going to write user routes and logic for creating a new user in the database.

As we know, users will write their email and password, it's a good practice to encrypt their password and save the encrypted password in the database.

For that purpose, we are going to install another npm package called `bcryptjs` .

Run the command `npm i bcryptjs` in the terminal.

First, we generate `salt` using `genSalt` function which comes with this package, with which we will hash the password using `hash` function. Now, when the user tries to log in we will compare this hashed password with the input password by using `compare` function present in the `bcryptjs` package.

Also, we are going to use `jsonwebtoken` package for stateless authentication, to learn more about `jsonwebtoken` you can go to [https://jwt.io/](https://jwt.io/).

For creating a token we use `sign` function and for verifying the token we use `verify` function. We will see the use of all these things in our `user.js` file. We will give this token in the request headers.

Earlier I told you that `Zod` for validating data, we are going to use Zod here. We use `Zod` for TypeScript-friendly schema validation, ensuring type safety and clean, reusable code with concise error reporting. Its declarative syntax and extensive features simplify data validation and serialization tasks in our applications. You can learn more about `Zod` on [https://zod.dev/](https://zod.dev/)

`user.js` file will look like this:-

```javascript
const { Router } = require("express");
const router = Router();
const zod = require("zod");
// User Schema from index.js file present in the db folder
const { User } = require("../db/index");
const jwt = require("jsonwebtoken");
const JWT_SECRET = "secretkey";
const bcrypt = require("bcryptjs");

// using zod for the validation checkpoint before signing in
const validateSignUpUser = zod.object({
  firstName: zod.string(),
  lastName: zod.string(),
  password: zod.string(),
  email: zod.string().email(),
});

// signing route for the user
router.post("/signUp", async (req, res) => {
  const firstName = req.body.firstName;
  const lastName = req.body.lastName;
  const password = req.body.password;
  const email = req.body.email;
   
// safeParsing the input fields using zod
  const isValid = validateSignUpUser.safeParse({
    firstName,
    lastName,
    password,
    email,
  });
// isValid return success in the form of true or false.

  try {
    // if true
    if (isValid.success) {
    // First we will check that is user is already present with that
    // email of not.
      const check = await User.find({ email });
      if (check.length !== 0) {
        res.status(411).json({
          success: false,
          message: "Email already taken / Incorrect Inputs",
        });
      } else {
        // generating salt
        const salt = await bcrypt.genSalt(10);
        // hashing the password
        const secPass = await bcrypt.hash(password, salt);
        // creating the user and saving the information in the database
        const user = await User.create({
          firstName,
          lastName,
          email,
          password: secPass,
        });
        
        const data = {
          user: {
            id: user.id,
          },
        };
        // creating a token using sign function
        const token = jwt.sign(data, JWT_SECRET);

        res.status(200).json({
          success: true,
          message: "User Created Successfully",
          token: token,
        });
      }
    }
  } catch (error) {
    res.status(411).json({
      success: false,
      message: "wrong Credentials",
    });
  }
});

// validation checkpoint logging in
const validateSignInUser = zod.object({
  email: zod.string().email(),
  password: zod.string(),
});

// log in route for the user
router.post("/logIn", async (req, res) => {
  const email = req.body.email;
  const password = req.body.password;

  const isValid = validateSignInUser.safeParse({
    email,
    password,
  });

  try {
    if (isValid.success) {
      const user = await User.findOne({ email });
      if (!user) {
        return res.status(400).json({
          success: false,
          message: "Please try to login with correct credentials",
        });
      }
      // comparing the hashed password and the input password
      const passwordCompare = await bcrypt.compare(password, user.password);
      if (!passwordCompare) {
        return res.status(400).json({
          success: false,
          message: "please try to login with correct credentials",
        });
      }
      const data = {
        user: {
          id: user.id,
        },
      };
      const token = jwt.sign(data, JWT_SECRET);
      res.status(200).json({
        token: token,
        success: true,
        message: "User logged in successfully",
      });
    }
  } catch (error) {
    res.status(411).json({
      success: false,
      message: "Error!!!",
    });
  }
});

module.exports = router;
```

And the routes for the user are created successfully. We have only created the `signup` and `login` routes. If you want to create routes for updating the details, getting user info, etc, you can create.

Don't you think, we should allow only authenticated users to upload images? Yes, you are right!!! We should. So, before the images route, we should make sure that only authenticated users are allowed. For that, we have to create a middleware.

In the `backend` folder, create another folder named `middleware` and create a file called `authMiddleware.js` . In this file, we will write a logic for giving access to only authenticated users.

Here we are going to use the token that we created while signing up (or logging in). The token would be present in the request headers.

`authMiddleware.js` file looks like this:-

```javascript
const jwt = require("jsonwebtoken");
const { User } = require("../db");
const { JWT_SECRET } = require("../config");

// middleware for checking user is authenticated or not
const authMiddleware = async (req, res, next) => {
  // getting the authorization header
  const header = req.headers.authorization;
  try {
    if (header.startsWith("Bearer ")) {
      const token = header.split("Bearer ")[1];
      // verifying the token
      const verify = jwt.verify(token, JWT_SECRET);
      if (verify) {
        const id = verify.user.id;
        // checking whether the user is present or not in the database
        const check = await User.findById(id);
        if (check) {
          // saving the user id the requests, we will understand in image
          // routes why do we need it.
          req.id = id;
          next();
        } else {
          res.status(404).json({});
        }
      }
    }
  } catch (error) {
    res.status(403).json({});
  }
};

module.exports = { authMiddleware };
```

Our backend server is almost ready, the only thing remaining is creating an image route.

1. **Using Cloudinary and Multer**
    

As we discussed earlier, we are going to save the image on Cloudinary, and the public link on the MongoDB database.

So, we have to install the `cloudinary` package. Also, we are going to use another library called `multer` for checking file uploading is an image. There are many use cases of `multer` but we are using it only for checking whether the uploaded file is an image.

We have to log on to `cloudinary.com` , and then go to the dashboard where you will see the API key, cloud name, and other necessary things for connecting it using node.js

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708437511156/b37b9b0d-bc52-4137-85d1-b3e3e1db8d2a.png align="center")

Make a config.js file in the backend folder, and save these keys into that file.

```javascript
const CLOUD_NAME = "cloudname";
const API_KEY = "apikey";
const API_SECRET = "secretapi";

module.exports = { CLOUD_NAME, API_KEY, API_SECRET };
```

Now we have to connect the `cloudinary` from our backend and have to write login for `multer` .

Create a `utils` folder in the `backend` , and create two files named `cloudinary.js` and `multer.js` .

`cloudinary.js` looks like this:

```javascript
const { CLOUD_NAME, API_KEY, API_SECRET } = require("../config");

const cloudinary = require("cloudinary").v2;
cloudinary.config({
  cloud_name: CLOUD_NAME,
  api_key: API_KEY,
  api_secret: API_SECRET,
});
module.exports = cloudinary;
```

`multer.js` looks like this:

```javascript
const multer = require("multer");
const path = require("path");

// for checking uploaded file is image
module.exports = multer({
  storage: multer.diskStorage({}),
  fileFilter: (req, file, cb) => {
    let ext = path.extname(file.originalname);
    if (ext !== ".jpg" && ext !== ".jpeg" && ext !== ".png") {
      cb(new Error("Unsupported file type!"), false);
      return;
    }
    cb(null, true);
  },
});
```

Now, let's create our most awaited image route.

In the `image.js` file, we will have two routes, one for uploading and one for getting the image details(i.e, public link, image id, etc)

`image.js` looks like this:

```javascript
const { Router } = require("express");
const router = Router();
const cloudinary = require("../utils/cloudinary");
const upload = require("../utils/multer");
const { Image } = require("../db/index");
const { authMiddleware } = require("../middleware/authMiddleware");

// getting all the images uploaded by an user
router.get("/getImages", authMiddleware, async (req, res) => {
  try {
    // we had saved user id in the request field with the help of the 
    // authMiddleware, now we are using it to find the images uploaded
    // by that user
    const images = await Image.find({ user: req.id });
    res.status(200).json({
      success: true,
      message: "Images received",
      images,
    });
  } catch (error) {
    res.status(404).json({
      success: false,
      message: "Can't find images",
    });
  }
});

// uploading an image
router.post(
  "/upload",
  authMiddleware,
  upload.single("image"),
  async (req, res) => {
    try {
      // uploading the image on the cloudinary
      const result = await cloudinary.uploader.upload(req.file.path);
      // creating the image doc
      const image = new Image({
        user: req.id,
        imageName: req.body.name,
        imageLink: result.secure_url,
        cloudinary_id: result.public_id,
      });
      // saving it to the database
      await image.save();
      res.status(200).json({
        success: true,
        image: image,
      });
    } catch (error) {
      res.status(404).json({
        success: false,
        message: "bad request",
      });
    }
  }
);

module.exports = router;
```

Yayyy!!! Our backend has been created successfully. You can test it on Postman or ThunderClient (Vs Code extension).

## 2\. Creating the FrontEnd

For now, we are not going to use transitions, animations, or any other fancy stuff. We will be keeping it simple and sorted. You can add styles if you want to.

I'll be using Bootstrap to save the time from creating the CSS.

In the `image-app` folder, create a `vite` react app named `frontend` .

If you are new to `vite`, then open a terminal in the `image-app` and run the command `npm create vite@latest` then you will be asked the Project name, write `frontend` and hit enter, now you have to select a framework select React

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708440027796/49f0f29f-ff5f-4aad-826d-5088136e59bf.png align="center")

then you will be asked to select a variant, select `JavaScript`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708440074429/65dc46a3-0515-4b1f-acb2-d1261b1f4508.png align="center")

You are ready to go...

Open it in the vs code.

We know React is all about making components and using them. So, we are going to make components.

Create a `components` folder in the `src` .

1. **Sign In component**
    
    Create `SignIn.jsx` in `components`
    
    It will contain a simple form. It will look like this:
    
    ```javascript
    import React, { useState } from "react";
    import { useNavigate } from "react-router-dom";
    
    const SignIn = () => {
      const [credentials, setCredentials] = useState({
        name: "",
        email: "",
        password: "",
        cpassword: "",
      });
      // we will use useNavigate for navigating from one page to another.
      let navigate = useNavigate();
    
      const handleSubmit = async (e) => {
        // logic for signIn
      };
    
      const onChange = (e) => {
        setCredentials({ ...credentials, [e.target.name]: e.target.value });
      };
      return (
        <div className="container my-3 bg-dark text-white rounded p-5">
          <h2>Create An Account</h2>
          <form onSubmit={handleSubmit}>
            <div className="mb-3">
              <label htmlFor="firstName" className="form-label">
                firstName
              </label>
              <input
                type="text"
                className="form-control"
                id="firstName"
                name="firstName"
                onChange={onChange}
                aria-describedby="firstName"
              />
            </div>
            <div className="mb-3">
              <label htmlFor="lastName" className="form-label">
                lastName
              </label>
              <input
                type="text"
                className="form-control"
                id="lastName"
                name="lastName"
                onChange={onChange}
                aria-describedby="lastName"
              />
            </div>
            <div className="mb-3">
              <label htmlFor="email" className="form-label">
                Email address
              </label>
              <input
                type="email"
                className="form-control"
                id="email"
                name="email"
                onChange={onChange}
                aria-describedby="emailHelp"
              />
            </div>
            <div className="mb-3">
              <label htmlFor="password" className="form-label">
                Password
              </label>
              <input
                type="password"
                className="form-control"
                name="password"
                id="password"
                onChange={onChange}
                minLength={5}
                required
              />
            </div>
            <div className="mb-3">
              <label htmlFor="cpassword" className="form-label">
                Confirm Password
              </label>
              <input
                type="password"
                className="form-control"
                id="cpassword"
                name="cpassword"
                onChange={onChange}
                minLength={5}
                required
              />
            </div>
            <button type="submit" className="btn btn-primary">
              Submit
            </button>
          </form>
        </div>
      );
    };
    
    export default SignIn;
    ```
    
2. **Log In component**
    
    Create `Login.jsx` in `components`
    
    It will also contain a simple form. You can write your own logic such that you can use one form for both sign-in and log-in. `Login.jsx` looks like this:
    
    ```javascript
    import React, { useState } from "react";
    import { useNavigate } from "react-router-dom";
    
    const Login = () => {
      const [credentials, setCredentials] = useState({
        email: "",
        password: "",
      });
      let navigate = useNavigate();
    
      const handleSubmit = async (e) => {
        // logic for login
      };
    
      const onChange = (e) => {
        setCredentials({ ...credentials, [e.target.name]: e.target.value });
      };
      return (
        <div className="container my-3 bg-dark text-white rounded p-5">
          <h2>Login to continue to myNotes</h2>
          <form onSubmit={handleSubmit}>
            <div className="mb-3">
              <label htmlFor="email" className="form-label">
                Email address
              </label>
              <input
                type="email"
                className="form-control"
                id="email"
                name="email"
                value={credentials.email}
                onChange={onChange}
                aria-describedby="emailHelp"
              />
            </div>
            <div className="mb-3">
              <label htmlFor="password" className="form-label">
                Password
              </label>
              <input
                type="password"
                className="form-control"
                name="password"
                value={credentials.password}
                onChange={onChange}
                id="password"
              />
            </div>
            <button type="submit" className="btn btn-primary">
              Submit
            </button>
          </form>
        </div>
      );
    };
    
    export default Login;
    ```
    
3. **Navbar component**
    
    Create `Navbar.jsx` in `components`
    
    We will create a navbar here, in which we will have Login, SignIn, and Logout buttons.
    
    ```javascript
    import React from "react";
    import { Link, useNavigate } from "react-router-dom";
    
    const Navbar = () => {
      let navigate = useNavigate();
    
      const handleLogout = () => {
        // logic for logging out
      };
    
      return (
        <nav className="navbar navbar-expand-lg navbar-dark bg-dark">
          <div className="container-fluid">
            <Link className="navbar-brand" to="/">
              myNotes
            </Link>
            <button
              className="navbar-toggler"
              type="button"
              data-bs-toggle="collapse"
              data-bs-target="#navbarSupportedContent"
              aria-controls="navbarSupportedContent"
              aria-expanded="false"
              aria-label="Toggle navigation"
            >
              <span className="navbar-toggler-icon"></span>
            </button>
            <div className="collapse navbar-collapse" id="navbarSupportedContent">
              <ul className="navbar-nav me-auto mb-2 mb-lg-0">
                <li className="nav-item">
                  <Link className="nav-link" aria-current="page" to="/">
                    Home
                  </Link>
                </li>
              </ul>
                <form className="d-flex" role="search">
                  <Link className="btn btn-primary me-1" to="/login" role="button">
                    Login
                  </Link>
                  <Link className="btn btn-primary mx-1" to="/signin" role="button">
                    SignUp
                  </Link>
                </form>
                <button onClick={handleLogout} className="btn btn-primary">
                  Log Out
                </button>
            </div>
          </div>
        </nav>
      );
    };
    
    export default Navbar;
    ```
    
4. **Image Component**
    
    Create ImageUpload.jsx in `components`
    
    Here we will create a form for uploading images, and at the end, we will write logic for retrieving them from the backend.
    
    ```javascript
    import React, { useEffect, useState } from "react";
    
    const ImageUpload = () => {
      const [image, setImage] = useState(null);
      const [name, setName] = useState("");
      const [imageArray, setImageArray] = useState([]);
      const [loading, setLoading] = useState(false);
    
      const handleFileChange = (e) => {
        setImage(e.target.files[0]);
      };
    
      const handleNameChange = (e) => {
        setName(e.target.value);
      };
    
      const fetchImages = async () => {
        //logic for retrieving image from the backend
      };
    
      useEffect(() => {
        // some logic for fetching images on the initial render
      }, []);
    
      const handleSubmit = async (e) => {
        // logic for uploading image on the backend
      };
    
      return (
        <>
          <div className="container my-3 bg-dark text-white rounded p-5">
            <form onSubmit={handleSubmit}>
              <div className="mb-3">
                <label htmlFor="name" className="form-label">
                  Name
                </label>
                <input
                  type="text"
                  className="form-control"
                  id="name"
                  name="name"
                  onChange={handleNameChange}
                  aria-describedby="name"
                />
              </div>
              <div className="mb-3">
                <label htmlFor="image" className="form-label">
                  Upload Image
                </label>
                <input
                  type="file"
                  className="form-control"
                  id="image"
                  name="image"
                  accept="image/png,image/jpg,image/jpeg"
                  onChange={handleFileChange}
                />
              </div>
              <button type="submit" className="btn btn-primary">
                Submit
              </button>
            </form>
          </div>
          <div className="row my-3">
            <h2 className=" text-white">Your Images</h2>
            <div className="container mx-2 text-white">
              {imageArray.length === 0 && "No Notes to display"}
            </div>
    
            {loading ? (
              <div className="text-white">Loading...</div>
            ) : (
              imageArray.map((image) => (
                <div key={image._id} className="col-md-4">
                  <div
                    className="card my-3 bg-secondary text-white rounded"
                    style={{ width: "18rem" }}
                  >
                    <img
                      src={image.imageLink}
                      style={{ objectFit: "cover", height: "18rem" }}
                      className="card-img-top"
                      alt="..."
                    />
                    <div className="card-body">
                      <p className="card-text">{image.imageName}</p>
                    </div>
                  </div>
                </div>
              ))
            )}
          </div>
        </>
      );
    };
    
    export default ImageUpload;
    ```
    
    The last thing remaining is establishing the routes on the frontend, for navigating the different components. For that, we have to edit `App.jsx` file. For creating routes we have to import `BrowserRouter`, `Routes`, `Route` from `react-router-dom` .
    
    Run `npm i react-router-dom` to install the package.
    
    ```javascript
    import "./App.css";
    import Login from "./components/Login";
    import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
    import SignIn from "./components/SignIn";
    import Navbar from "./components/Navbar";
    import ImageUpload from "./components/ImageUpload";
    
    function App() {
      return (
        <>
          <Router>
            <Navbar />
            <div className="container">
              <Routes>
                <Route path="/" element={<ImageUpload />} />
                <Route path="/signIn" element={<SignIn />} />
                <Route path="/login" element={<Login />} />
              </Routes>
            </div>
          </Router>
        </>
      );
    }
    
    export default App;
    ```
    

## 3\. Integrating the BackEnd with the FrontEnd

Our web app is almost complete, we have to integrate the backend with the frontend only.

To send the requests or to receive the responses from the backend, we use `fetch` or `axios` . For using `axios` we have to install it, using `npm i axios` command. `fetch` is a modern `JavaScript API` for making `HTTP` requests. We will use `fetch` in our project.

1. **Sign-in logic**
    
    Open the `SignIn.jsx` file.
    
    sign-in request is post request, so while using `fetch` API, we have to tell what the method is ( post, put, delete, etc.).
    
    When we send the request to the backend it will return a token, we will save that token in the localStorage.
    
    edit the `handleSubmit` function that we defined earlier in the file.
    
    ```javascript
    const handleSubmit = async (e) => {
        e.preventDefault();
        const { firstName, lastName, email, password } = credentials;
        const response= await fetch("http://localhost:3000/api/v1/user/signUp",
         {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ firstName, lastName, email, password }),
        });
        const json = await response.json();
        console.log(json);
        if (json.success) {
          // save the auth token and redirect
          localStorage.setItem("authorization", json.authtoken);
          navigate("/");
        } else {
          navigate("/signIn");
        }
      };
    ```
    
2. **Log-in logic**
    
    Open the `Login.jsx` file.
    
    A log-in request is also a post request, so we will do the same as we did for the sign-in logic.
    
    edit the `handleSubmit` function in the `Login.jsx` file
    
    ```javascript
    const handleSubmit = async (e) => {
        e.preventDefault();
        const response = await fetch("http://localhost:3000/api/v1/user/login",
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            email: credentials.email,
            password: credentials.password,
          }),
        });
        const json = await response.json();
        console.log(json);
        if (json.success) {
          localStorage.setItem("authorization", json.token);
          navigate("/");
        } else {
          navigate("/login");
        }
      };
    ```
    
3. **Image Uploading and fetching**
    
    This is a bit tricky, but very easy if we understand it once.
    
    Let's start with the uploading logic, as we were sending the inputs for sign-in and log-in requests in the form of `JSON`. `JSON` is an excellent format for sending unstructured data but it can't handle file uploads and that's where the `FormData` comes into action. `FormData` allows us to send different types of files. So, what are we waiting for? Let's code...
    
    Edit the `handleSubmit` function in the `ImageUpload.jsx`
    
    ```javascript
    const handleSubmit = async (e) => {
        console.log(e);
        e.preventDefault();
        try {
          setLoading(true);
          // create FormData() instance
          const formData = new FormData();
          // adding values to the formData using append method
          formData.append("image", image);
          formData.append("name", name);
    
          const response = await fetch(
            "http://localhost:3000/api/v1/image/upload",
            {
              method: "POST",
              body: formData,
              headers: {
              Authorization: `Bearer ${localStorage.getItem("authorization")}`,
              },
            }
          );
    
          const json = await response.json();
          setImageArray(imageArray.concat(json.image));
           // resetting the form after submitting
          e.target[1].value = null;
          e.target[0].value = "";
          setImage(null);
          setName("");
          setLoading(false);
        } catch (error) {
          console.log(error);
        }
      };
    ```
    
    Image uploading is done, now we have to work on fetching the images from the server.
    
    When a user is directed to this route, that page must fetch the image on its first rendering and `useEffect` hook is going to help us.
    
    Edit the `fetchImages` function and write the logic for the `useEffect`, we are going to use `fetchImages` function inside the `useEffect` hook.
    
    ```javascript
    const fetchImages = async () => {
        const response = await fetch(
          "http://localhost:3000/api/v1/image/getImages",
          {
            method: "GET",
            headers: {
           Authorization:`Bearer ${localStorage.getItem("authorization")}`,
            },
          }
        );
        const json = await response.json();
        if (json.success) {
          setImageArray(json.images);
        }
      };
    ```
    
    ```javascript
    useEffect(() => {
        // if user is logged in then fetch the images otherwise
        // redirect the user to the login page
        if (localStorage.getItem("authorization")) {
          fetchImages();
        } else {
          navigate("/login");
        }
      }, []);
    ```
    
4. **Fixing the Navbar**
    
    Open the `Navbar.jsx` file.
    
    In the Navbar component, we have to write the logic for the logout and it is the easiest of all the logic we have implemented till now. We have to just remove the token from the `localStorage`.
    
    Edit the `handleLogout` Function
    
    ```javascript
    const handleLogout = () => {
        localStorage.removeItem("authorization");
        navigate("/login");
      };
    ```
    
    The last thing remaining in the Navbar component is to show and hide the sign-in, log-in, and log-out buttons.
    
    ```javascript
    {
      !localStorage.getItem("authorization") ? (
       <form className="d-flex" role="search">
          <Link className="btn btn-primary me-1" to="/login" role="button">
               Login
           </Link>
           <Link className="btn btn-primary mx-1" to="/signin" role="button">
                SignUp
           </Link>
        </form>
        ) : (
           <button onClick={handleLogout} className="btn btn-primary">
                 Log Out
           </button>
        )
    }
    ```
    

That's it!!!

Our Full-stack application has been made and fully working. The only task on your end is to deploy it, or you can wait for me until I write a blog for that 😂😂😂.

Thank You, and make sure to give feedback and like.
