Bookstore Ecommerce App using MERN Stack

Last Updated : 23 Jul, 2025

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%E2%80%99 },
		
		];
		
		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:

Comment

Explore