In this article, we'll walk through creating a Product Review Platform using the MERN stack (MongoDB, Express.js, React, and Node). By the end of this guide, you'll have a functional application where users can add products, leave reviews, and delete products.
Preview of final output: Let us have a look at how the final output will look like.

Prerequisites:
Approach to create Product Review Platform:
- State and Components:
- Uses
useStateforproductsandnewProduct. - Renders product cards, reviews, and forms.
- Uses
- API Interaction:
- Fetches products on mount.
- Utilizes Axios for CRUD operations.
- Product Operations:
- Adds new products.
- Deletes products.
- Submits and displays reviews.
- User Interface:
- Forms for input.
- Dynamically updates UI.
Steps to Create the Frontend:
Step 1: Set up React frontend using the command.
npx create-react-app clientStep 2: Navigate to the project folder using the command.
cd clientStep 3: Install axios
npm i axiosProject Structure:

The updated dependencies in package.json for frontend will look like:
"dependencies": {
"axios": "^1.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: Below is the client code.
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
}
.app-container {
text-align: center;
}
.product-card {
border: 1px solid #ddd;
border-radius: 11px;
box-shadow: 0 8px 35px rgba(0, 0, 0, 0.1);
padding: 10px;
margin: 11px;
width: 300px;
}
.product-card h2 {
margin-bottom: 10px;
color: #333;
}
.product-card p {
color: #666;
}
.product-card h3 {
margin: 10px 0;
color: #333;
}
.product-card ul {
list-style-type: none;
padding: 0;
}
.product-card ul li {
margin-bottom: 2px;
color: #555;
}
.product-card form {
margin-top: 10px;
}
.product-card label {
display: block;
margin-bottom: 5px;
color: #333;
gap: 10px;
}
.product-card input,
.product-card textarea {
width: 100%;
padding: 3px;
margin-bottom: px;
box-sizing: border-box;
}
label {
margin: 10px;
}
.product-card button {
background-color: #007BFF;
color: #fff;
border: none;
padding: 10px;
cursor: pointer;
border-radius: 4px;
}
.product-card button:hover {
background-color: #0056b3;
}
@media (max-width: 600px) {
.product-card {
width: 100%;
}
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
max-width: 1200px;
margin: 20px auto;
}
.delete-btn {
background-color: red;
color: white;
margin-bottom: 15px;
}
.add-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
text-align: center;
background-color: rgb(8, 149, 8);
color: white;
width: 70vw;
padding: 5px;
border-radius: 10px;
font-size: larger;
margin: 20px;
}
h2 {
text-align: center;
margin-bottom: -5px;
margin-top: -5px;
}
h3 {
color: #007BFF;
}
.outer-cont {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.add-btn {
background-color: rgb(8, 149, 8);
color: #121010;
margin-left: 5px;
border: none;
padding: 5px 10px;
border-radius: 10px;
}
img {
width: 200px;
height: 200px;
}
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./App.css";
const App = () => {
const [products, setProducts] = useState([]);
const [newProduct, setNewProduct] = useState({
name: "",
description: "",
image: "",
});
useEffect(() => {
// Fetch products from the server
axios
.get("http://localhost:5000/api/products")
.then((response) => setProducts(response.data))
.catch((error) => console.error(error));
}, []);
const handleReviewSubmit = (productId, review) => {
// Submit a new review for a product
axios
.post(
`http://localhost:5000/api/products/${productId}/review`, review)
.then((response) => {
// Update the products in the state with the new review
const updatedProducts = products.map((product) =>
product._id === productId ? response.data : product
);
setProducts(updatedProducts);
})
.catch((error) => console.error(error));
};
const handleAddProduct = () => {
// Submit a new product
console.log("i am called");
axios
.post("http://localhost:5000/api/products", newProduct)
.then((response) => {
// Update the products in the state with the new product
setProducts([...products, response.data]);
// Clear the newProduct state for the next entry
setNewProduct({ name: "", description: "", image: "" });
})
.catch((error) => console.error(error));
};
const handleProductDelete = (productId) => {
// Send a DELETE request to the server
axios
.delete(`http://localhost:5000/api/products/${productId}`)
.then((response) => {
// Update the products in the state after successful deletion
console.log(
"The Producted deleted successfully was:",
response.data.deletedProduct
);
const updatedProducts = products.filter(
(product) => product._id !== productId
);
setProducts(updatedProducts);
})
.catch((error) =>
console.error(
`Error deleting product with ID
${productId}:`, error)
);
};
return (
<div className="outer-cont">
<h1
style={
{
marginTop: "10px",
color: "white"
}}>
GFG
</h1>
<h2>Product Review Platform</h2>
<div className="add-container">
<h3>Add a New Product:</h3>
<form
onSubmit={(e) => {
e.preventDefault();
handleAddProduct();
}}>
<label>
Name:{" "}
<input
type="text"
name="name"
value={newProduct.name}
onChange={(e) =>
setNewProduct(
{
...newProduct,
name: e.target.value
}
)
}
required
/>
</label>
<label>
Description:{" "}
<input
type="text"
name="description"
value={newProduct.description}
onChange={(e) =>
setNewProduct(
{
...newProduct,
description: e.target.value
}
)
}
required
/>
</label>
<label>
{" "}
Image URL:{" "}
<input
type="text"
name="image"
value={newProduct.image}
onChange={(e) =>
setNewProduct(
{
...newProduct,
image: e.target.value
})
}
required
/>
</label>
<button className="add-btn"
type="submit">
Add Product
</button>
</form>
</div>
<div className="cards">
{products.map((product) => (
<div key={product._id}
className="product-card">
<h2>{product.name}</h2>
<button
className="delete-btn"
onClick={
() =>
handleProductDelete(product._id)
}>
Delete Product
</button>
<img src={product.image}
style={{ width: "300px" }} alt="" />
<p>{product.description}</p>
<h3>Reviews:</h3>
<ul>
{product.reviews.map((review, index) => (
<li key={index}>
<strong>{review.user}</strong>
-
{review.rating}/5:{" "}
{review.comment}
</li>
))}
</ul>
<h3>Add a Review:</h3>
<form
onSubmit={(e) => {
e.preventDefault();
const user = e.target.elements.user.value;
const rating = e.target.elements.rating.value;
const comment = e.target.elements.comment.value;
handleReviewSubmit(product._id,
{ user, rating, comment });
}}>
<label>
User: <input type="text" name="user" required />
</label>
<label>
Rating:{" "}
<input type="number"
name="rating" min="1"
max="5" required />
</label>
<label>
Comment: <textarea name="comment" required>
</textarea>
</label>
<button type="submit">
Submit Review
</button>
</form>
</div>
))}
</div>
</div>
);
};
export default App;
Step to Run Your React App
npm startYour React app should now be running at http://localhost:3000. It displays a form for adding new products and a list of existing products with their reviews.
Steps to Create the Backend:
Step 1: Create a directory for project
npm init backendStep 2: Open project using the command
cd backendStep 3: Installing the required packages
npm install express mongoose cors body-parserProject Structure:

The updated dependencies in package.json file for backend will look like:
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}
Example: Create a new file `server.js` and write the provided Nodejs code.
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
// Middleware for JSON parsing
app.use(bodyParser.json());
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/product_review', {
useNewUrlParser: true,
useUnifiedTopology: true,
}).then(() => {
console.log('Connected to MongoDB')
});
// Define the Product schema
const productSchema = new mongoose.Schema({
name: String,
description: String,
image: String,
reviews: [
{
user: String,
rating: Number,
comment: String,
},
],
});
const Product = mongoose.model('Product', productSchema);
// API endpoints
// Route to add a new product
app.post('/api/products', async (req, res) => {
try {
const { name, description, image } = req.body;
// Validate request data
if (!name || !description || !image) {
return res.status(400).json(
{
message: 'Incomplete product data'
}
);
}
// Create a new product
const newProduct = new Product({
name,
description,
image,
reviews: [],
});
// Save the new product to the database
const savedProduct = await newProduct.save();
// Respond with the newly added product
res.status(201).json(savedProduct);
} catch (error) {
console.error('Error adding product:', error);
res.status(500)
.json(
{
message: 'Internal Server Error'
}
);
}
});
app.get('/api/products', async (req, res) => {
try {
const products = await Product.find();
res.json(products);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
app.post('/api/products/:id/review', async (req, res) => {
const { user, rating, comment } = req.body;
try {
const product =
await Product.findById(req.params.id);
product.reviews
.push(
{
user, rating,
comment
}
);
await product.save();
res.status(201).json(product);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
// Delete a product by ID
app.delete('/api/products/:id', async (req, res) => {
const productId = req.params.id;
try {
// Find the product by ID and delete it from the database
const deletedProduct =
await Product.findByIdAndDelete(productId);
if (!deletedProduct) {
return res.status(404)
.json(
{
message: 'Product not found'
}
);
}
res.json(
{
message: 'Product deleted',
deletedProduct
}
);
} catch (error) {
console.error('Error deleting product:', error);
res.status(500)
.json(
{
message: 'Internal Server Error'
}
);
}
});
app.listen(PORT,
() => {
console.log(`Server is running on port ${PORT}`);
});
Steps to run the server:
node server.js Output: Your server should now be running at http://localhost:5000
Output: Data saved in Database
