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:
 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:
 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:
 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:
|