In this comprehensive guide, we’ll walk through the step-by-step process of creating a Fruit and Vegetable Market Shop 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 fruits and vegetables.
Preview of final output: Let us have a look at how the final output will look like.
Prerequisites:
Approach to create Fruit and Vegetable Market Shop in MERN:
-
Import Statements:
- Import necessary dependencies and components.
- React is imported for defining React components.
-
ProductList
andHeader
are custom components, assumed to be present in the./components
directory. -
CustomItemContext
is imported, presumably a custom context provider.
-
Functional Component:
-
Define a functional component named
App
.
-
Define a functional component named
-
Context Provider:
-
Wrap the
Header
andProductList
components inside theCustomItemContext
provider. This suggests that the components within this provider have access to the context provided byCustomItemContext
.
-
Wrap the
-
Component Rendering:
-
Render the following components:
-
CustomItemContext
: Presumably, this is a context provider that wraps its child components (Header
andProductList
). The purpose of this context is not clear from the provided code snippet. -
Header
component. -
ProductList
component.
-
-
Render the following components:
Steps to Create the Backend:
Step 1: Create a directory for project
npm init backend
Step 2: Open project using the command
cd backend
Step 3: Installing the required packages
npm install express mongoose cors
Project Structure:
The updated dependencies in package.json file for backend will look like:
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}
Example: Create `server.js` and paste the below code.
// server.js const express = require( 'express' );
const mongoose = require( 'mongoose' );
const app = express(); const PORT = process.env.PORT || 5000; const cors = require( 'cors' );
{
useNewUrlParser: true ,
useUnifiedTopology: true
}
); app.use(express.json()); app.use(cors()); // Use the cors middleware
const productSchema = new mongoose.Schema({
name: String,
type: String,
description: String,
price: Number,
image: String,
}); const Product = mongoose.model( 'Product' , productSchema);
// Function to seed initial data into the database const seedDatabase = async () => { try {
await Product.deleteMany(); // Clear existing data
const products = [
{
name: 'Apple' , type: 'Fruit' ,
description: 'Fresh and crispy' ,
price: 150,
image:
},
{
name: 'Banana' ,
type: 'Fruit' ,
description: 'Rich in potassium' ,
price: 75,
image:
},
{
name: 'Orange' ,
type: 'Fruit' ,
description: 'Packed with vitamin C' ,
price: 200,
image:
},
{
name: 'Carrot' ,
type: 'Vegetable' ,
description: 'Healthy and crunchy' ,
price: 100,
image:
},
{
name: 'Broccoli' ,
type: 'Vegetable' ,
description: 'Nutrient-rich greens' ,
price: 175,
image:
},
{
name: 'Grapes' ,
type: 'Fruit' ,
description: 'Sweet and juicy' ,
price: 250,
image:
},
{
name: 'Strawberry' ,
type: 'Fruit' ,
description: 'Delicious red berries' ,
price: 300,
image:
},
{
name: 'Lettuce' ,
type: 'Vegetable' ,
description: 'Crisp and fresh' ,
price: 120,
image:
},
{
name: 'Tomato' ,
type: 'Vegetable' ,
description: 'Versatile and flavorful' ,
price: 180,
image:
},
{
name: 'Cucumber' ,
type: 'Vegetable' ,
description: 'Cool and hydrating' ,
price: 130,
image:
},
];
await Product.insertMany(products);
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 products app.get( '/api/products' , async (req, res) => {
try {
// Fetch all products from the database
const allProducts = await Product.find();
// Send the entire products array as JSON response
res.json(allProducts);
} 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:
node server.js
Steps to Create the Frontend:
Step 1: Set up React frontend using the command.
npx create-react-app client
Step 2: Navigate to the project folder using the command.
cd client
Project Structure:
The updated dependencies in package.json for frontend will look like:
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
// 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;
|
//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 =
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;
|
// client/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 Fruit & Vegetable Market
</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;
|
// 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/Kg
</p>
<button onClick={
() => handleAddToCart(product)
}>
Add to Cart
</button>
<button onClick={
() =>
handleRemoveToCart(product)
}>
-
</button>
</div>
</div>
);
}; export default ProductItem;
|
// 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.type === selectedType);
setSortedProducts(filtered);
}
};
return (
<div className= 'prdt-list' >
<h2>Product 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= 'Fruit' >Fruit</option>
<option value= 'Vegetable' >Vegetable</option>
</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;
|
/*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.3 s 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;
}
h 2 {
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 : 2 rem;
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 ;
}
|
To start frontend code:
npm start
Output:
Output of Data Saved in Database: