Horje
Bookstore Ecommerce App using MERN Stack

Bookstore E-commerce project is a great way to showcase your understanding of full-stack development. In this article, we’ll walk through the step-by-step process of creating a Bookstore E-commerce using the MERN (MongoDB, Express.js, React, Node.js) stack. This project will showcase how to set up a full-stack web application where users can view, filter, and purchase various books.

Preview of final output: Let us have a look at how the final application will look like:

Screenshot-2567-01-04-at-233834

Final Project Output

Prerequisites:

Approach to create Bookstore Ecommerce:

  • List all the requirement for the project and make the flowand structure of the project accordingly.
  • Chooses the required dependencies and requirement which are more suitable for the project.
  • ProductList and Header are custom components, assumed to be present in the ./components directory.
  • CustomItemContext is imported, presumably a custom context provider.
  • Define a functional component named App.
  • Wrap the Header and ProductList components inside the CustomItemContext provider. This suggests that the components within this provider have access to the context provided by CustomItemContext.
  • CustomItemContext: Presumably, this is a context provider that wraps its child components (Header and ProductList). The purpose of this context is not clear from the provided code snippet.

Steps to Create the Backend:

Step 1: Create a directory for project

mkdir server
cd server

Step 2: Initialized the Express app and installing the required packages

npm init -y
npm i express mongoose cors

Project Structure:

Screenshot-2567-01-04-at-234108

Backend project structure

The updated dependencies in package.json file of backend will look like:

"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}

Example: Create `server.js` and write the below code.

JavaScript
// server.js

const express = require('express');
const mongoose = require('mongoose');
const app = express();
const PORT = process.env.PORT || 5000;
const cors = require('cors');

mongoose.connect('<Your connection string>', { useNewUrlParser: true, useUnifiedTopology: true });

app.use(express.json());
app.use(cors()); // Use the cors middleware

const bookSchema = new mongoose.Schema({
    title: String,
    author: String,
    genre: String,
    description: String,
    price: Number,
    image: String,
});

const Book = mongoose.model('Book', bookSchema);

// Function to seed initial data into the database
const seedDatabase = async () => {
    try {
        await Book.deleteMany(); // Clear existing data

        const books = [
            { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', genre: 'Fiction', description: 'A classic novel about the American Dream', price: 20, image: 'https://media.geeksforgeeks.org/wp-content/uploads/20240110011815/sutterlin-1362879_640-(1).jpg' },
            { title: 'To Kill a Mockingbird', author: 'Harper Lee', genre: 'Fiction', description: 'A powerful story of racial injustice and moral growth', price: 15, image: 'https://media.geeksforgeeks.org/wp-content/uploads/20240110011854/reading-925589_640.jpg' },
            { title: '1984', author: 'George Orwell', genre: 'Dystopian', description: 'A dystopian vision of a totalitarian future society', price: 255, image: 'https://media.geeksforgeeks.org/wp-content/uploads/20240110011929/glasses-1052010_640.jpg' },
            { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', genre: 'Fiction', description: 'A classic novel about the American Dream', price: 220, image: 'https://media.geeksforgeeks.org/wp-content/uploads/20240110011929/glasses-1052010_640.jpg' },
            { title: 'To Kill a Mockingbird', author: 'Harper Lee', genre: 'Fiction', description: 'A powerful story of racial injustice and moral growth', price: 1115, image: 'https://media.geeksforgeeks.org/wp-content/uploads/20240110011929/glasses-1052010_640.jpg' },
            { title: '1984', author: 'George Orwell', genre: 'Dystopian', description: 'A dystopian vision of a totalitarian future society', price: 125, image: 'https://media.geeksforgeeks.org/wp-content/uploads/20240110011929/glasses-1052010_640.jpg’ },
        
        ];
        
        await Book.insertMany(books);
        console.log('Database seeded successfully');
    } catch (error) {
        console.error('Error seeding database:', error);
    }
};

// Seed the database on server startup
seedDatabase();

// Define API endpoint for fetching all books
app.get('/api/books', async (req, res) => {
    try {
        // Fetch all books from the database
        const allBooks = await Book.find();

        // Send the entire books array as JSON response
        res.json(allBooks);
    } catch (error) {
        console.error(error);
        res.status(500).json({ error: 'Internal Server Error' });
    }
});

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

Start the backend server with the following command:

node server.js

Steps to Create the Frontend:

Step 1: Set up React frontend using the command.

npx create-react-app client
cd client

Step 2: Install the required dependencies.

npm i @fortawesome/free-solid-svg-icons
npm i @fortawesome/react-fontawesome

Project Structure:

Screenshot-2567-01-05-at-002713

Frontend project structure

The updated dependencies in package.json file of frontend will look like:

"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Create the required files and write the following code.

CSS
/*App.css*/

.cart-items {

    border-radius: 50%;
    background-color: rgb(20, 158, 105);
    font-weight: 700;
    color: aliceblue;
    width: 30px;
    height: 30px;
    font-size: 30px;
    padding: 10px;
    top: 10px;
    position: relative;
    left: 30px;
}

.header {
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    padding: 10px;
    border-bottom: 1px sold #ccc;

}


/* card */
/* client/src/components/ProductItem.css */
.product-card {
    border: 1px solid #ddd;
    border-radius: 8px;
    width: fit-content;
    padding: 16px;
    margin: 16px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    background-color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.product-image {
    width: 200px;
    height: 200px;
    object-fit: cover;
    border-radius: 10px;
    margin-bottom: 12px;
    transition: transform 0.3s ease-in-out;
}

.product-image:hover {
    transform: scale(1.1);
    /* Enlarge the image on hover */
}

.product-details {
    text-align: center;
}


.item-card {
    display: flex;
    flex-wrap: wrap;
}

h2 {
    text-align: center;
}

.filter-btn {
    display: flex;
    flex-direction: row;
    padding: 10px;
    gap: 10px;

    justify-content: center;


}

.prdt-list {
    display: flex;
    flex-direction: column;
    justify-content: center;

}

.cart-num {
    margin-bottom: 40px;
    cursor: pointer;
}


.buy-now-btn {
    background-color: rgb(11, 162, 11);
    color: white;
    padding: 5px 10px;
    border-radius: 10px;
    font-size: 2rem;
    position: fixed;
    top: 30%;
    right: 10px;
    cursor: pointer;
}

.buy-now-btn:hover {
    background-color: rgb(113, 230, 113);
    color: brown;
}

.gfg {
    background-color: green;
    color: white;
    padding: 5px 10px;
    border-radius: 10px;
}
JavaScript
// client/src/App.js
import React from 'react';

import ProductList from './components/ProductList';
import Header from './components/Header';
import './App.css'// client/src/App.js
import React from 'react';

import ProductList from './components/ProductList';
import Header from './components/Header';
import './App.css'
import CustomItemContext from './context/ItemContext';

const App = () => {
    return (
        <CustomItemContext>
            <Header />
            <ProductList />
        </CustomItemContext>
    );
};

export default App;
import CustomItemContext from './context/ItemContext';

const App = () => {
    return (
        <CustomItemContext>
            <Header />
            <ProductList />
        </CustomItemContext>
    );
};

export default App;
JavaScript
// src/context/ItemContext.js

import { createContext, useEffect, useState } from "react";

const itemContext = createContext();

// creating custom provider
function CustomItemContext({ children }) {
    const [products, setProducts] = useState([]);
    const [cart, setCart] = useState([]);
    const [itemsInCart, setItemsInCart] = useState(0);
    const [totalPrice, setTotalPrice] = useState(0);

    // useEffect to load all the vegetables
    useEffect(() => {
        // Fetch products from the backend and dispatch 'SET_PRODUCTS' action
        const fetchData = async () => {
            const response = await fetch("http://localhost:5000/api/books");
            const products = await response.json();
            console.log(products);
            setProducts(products);
        };

        fetchData();
    }, []);

    const addToCart = (product) => {
        setTotalPrice(totalPrice + product.price);
        setCart([...cart, product]);
        setItemsInCart(itemsInCart + 1);
    };

    const removeFromCart = (product) => {
        const index = cart.findIndex((prdt) => prdt._id === product._id);
        console.log(index);

        if (index !== -1) {
            // Item found in the cart
            // Now you can remove it from the cart array
            const updatedCart = [...cart];
            updatedCart.splice(index, 1);
            setTotalPrice(totalPrice - cart[index].price);
            setCart(updatedCart);
            setItemsInCart(itemsInCart - 1);
        } else {
            console.log("Item not found in the cart");
        }
    };

    return (
        // default provider
        <itemContext.Provider
            value={{
                products,
                addToCart,
                removeFromCart,
                itemsInCart,
                totalPrice,
            }}
        >
            {children}
        </itemContext.Provider>
    );
}

export { itemContext };
export default CustomItemContext;
JavaScript
// src/components/Header.js

import React, { useContext } from "react";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCartShopping } from "@fortawesome/free-solid-svg-icons";
import { itemContext } from "../context/ItemContext";

const Header = () => {
    const { itemsInCart, totalPrice } = useContext(itemContext);

    return (
        <div className="header">
            <h1 className="gfg">GFG Book Store </h1>
            <h3 style={{ color: "green" }}>Total Price: {totalPrice}</h3>

            <div className="cart-num">
                <div className="cart-items">{itemsInCart}</div>

                <FontAwesomeIcon icon={faCartShopping} size="4x" />
            </div>
        </div>
    );
};

export default Header;
JavaScript
// client/src/components/ProductItem.js

import React, { useContext } from "react";
import { itemContext } from "../context/ItemContext";

const ProductItem = ({ product }) => {
    const { addToCart, removeFromCart } = useContext(itemContext);

    const handleAddToCart = (product) => {
        console.log(product);
        addToCart(product);
    };

    const handleRemoveToCart = (product) => {
        console.log("product removed", product);
        removeFromCart(product);
    };

    return (
        <div className="product-card">
            <img
                className="product-image"
                src={product.image}
                alt={product.name}
            />

            <div className="product-details">
                <h3 style={{ fontWeight: "700" }}>{product.name}</h3>
                <p style={{ fontWeight: "300" }}>{product.description}</p>
                <p style={{ fontWeight: "500" }}>Price: {product.price} Rs</p>
                <p>{product.genre}</p>
                <p style={{ fontWeight: "700", color: "brown" }}>
                    {product.author}
                </p>

                <button onClick={() => handleAddToCart(product)}>
                    Add to Cart
                </button>

                <button onClick={() => handleRemoveToCart(product)}>-</button>
            </div>
        </div>
    );
};

export default ProductItem;
JavaScript
// client/src/components/ProductList.js

import React, { useContext, useEffect, useState } from "react";
import ProductItem from "./ProductItem";
import { itemContext } from "../context/ItemContext";

const ProductList = () => {
    const { products } = useContext(itemContext);
    // Keep a local state for sorted products
    const [sortedProducts, setSortedProducts] = useState([...products]);     
    const [minPrice, setMinPrice] = useState(0);
    const [maxPrice, setMaxPrice] = useState(3000);
    // 'all' represents no type filter
    const [selectedType, setSelectedType] = useState("all"); 
    
    useEffect(() => {
        setSortedProducts([...products]);
    }, [products]);

    const handleSortByPrice = () => {
        const sorted = [...sortedProducts].sort((a, b) => a.price - b.price);
        setSortedProducts(sorted);
    };

    const handleFilterByPriceRange = () => {
        const filtered = products.filter(
            (product) => product.price >= minPrice && product.price <= maxPrice
        );
        setSortedProducts(filtered);
    };

    const handleFilterByType = () => {
        if (selectedType === "all") {
            // Reset the type filter
            setSortedProducts([...products]);
        } else {
            const filtered = products.filter(
                (product) => product.genre === selectedType
            );
            setSortedProducts(filtered);
        }
    };

    return (
        <div className="prdt-list">
            <h2 style={{ color: "green" }}>Book List</h2>
            <div className="filter-btn">
                <button onClick={handleSortByPrice}>Sort by Price</button>
                <label>
                    Min Price:
                    <input
                        type="number"
                        value={minPrice}
                        onChange={(e) => setMinPrice(Number(e.target.value))}
                    />
                </label>
                <label>
                    Max Price:
                    <input
                        type="number"
                        value={maxPrice}
                        onChange={(e) => setMaxPrice(Number(e.target.value))}
                    />
                </label>
                <button onClick={() => handleFilterByPriceRange()}>
                    Filter by Price Range
                </button>
                <label>
                    Filter by Type:
                    <select
                        value={selectedType}
                        onChange={(e) => setSelectedType(e.target.value)}
                    >
                        <option value="all">All</option>
                        <option value="Fiction">Fiction</option>
                        <option value="Dystopian">Dystopian</option>
                        {/* Add more options as needed */}
                    </select>
                </label>

                <button onClick={handleFilterByType}>Filter by Type</button>
            </div>

            <ul className="item-card">
                {sortedProducts.map((product) => (
                    <ProductItem key={product._id} product={product} />
                ))}
            </ul>
            <div className="buy-now-btn">Buy Now</div>
        </div>
    );
};

export default ProductList;

To start frontend code:

npm start

Output:




Reffered: https://www.geeksforgeeks.org


Geeks Premier League

Related
Notes Maker App using MERN Stack Notes Maker App using MERN Stack
Bag-Of-Words Model In R Bag-Of-Words Model In R
Understanding React State and Data Flow Understanding React State and Data Flow
Hangman game using React Hangman game using React
Difference between Postman Collections and Environments. Difference between Postman Collections and Environments.

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
14