MERN Stack Application Of product Listing in an Ecommerce Application
Making a MERN (MongoDB, Express.js, React.js, Node.js) stack app for product listing in an online store requires building the frontend with React, creating a backend with Node.js and Express, and using MongoDB for the database. I'll give you a simple example to help you begin.
1. Set up your project:
# Create a new React app
npx create-react-app e-commerce-client
# Create a new Node.js/Express app
mkdir e-commerce-server
cd e-commerce-server
npm init -y
npm install express mongoose cors
# Create a MongoDB database
# You can use MongoDB Atlas or a local MongoDB installation
2. Backend (Node.js/Express):
Create a file server.js
in the e-commerce-server
directory:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
mongoose.connect('mongodb://localhost:27017/e-commerce', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const productSchema = new mongoose.Schema({
name: String,
price: Number,
description: String,
});
const Product = mongoose.model('Product', productSchema);
app.get('/api/products', async (req, res) => {
try {
const products = await Product.find();
res.json(products);
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
3. Frontend (React):
Edit the src/App.js
file in the e-commerce-client
directory:
import React, { useState, useEffect } from 'react';
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products')
.then((res) => res.json())
.then((data) => setProducts(data))
.catch((error) => console.error('Error fetching data:', error));
}, []);
return (
<div>
<h1>E-commerce Product Listing</h1>
<ul>
{products.map((product) => (
<li key={product._id}>
<h3>{product.name}</h3>
<p>{product.description}</p>
<p>${product.price}</p>
</li>
))}
</ul>
</div>
);
}
export default App;
4. Run your application.
Make sure your MongoDB server is running. In the e-commerce-server
directory, run:
node server.js
In the e-commerce-client
directory, run:
npm start
Your browser will open, and you'll see your e-commerce product listing application.
This is a simple example, and you can make it better by adding features like product details, authentication, cart functions, and more. Also, take care of security parts like input validation, authentication, and authorization in a real-world setting.
Product details, Authentication, and Cart functionality; add these functionalities to the above
To add product details, authentication, and cart functionality, you'll need to work on both the frontend (React) and backend (Node.js/Express) of your MERN stack app. I'll show you how to do it below.
1. Backend (Node.js/Express):
Update the server.js
file in the e-commerce-server
directory to include routes for product details and authentication.
// ... (previous code)
// Add the product details route
app.get('/api/products/:id', async (req, res) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json(product);
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Authentication route (basic example, not secure - for demonstration purposes)
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
// You would typically validate credentials against a database
if (username === 'demo' && password === 'password') {
res.json({ success: true, message: 'Login successful' });
} else {
res.status(401).json({ success: false, message: 'Invalid credentials' });
}
});
// ... (previous code)
2. Frontend (React):
Change the src/App.js
file in the e-commerce-client
folder to add product details, login features, and basic cart functions.
import React, { useState, useEffect } from 'react';
function App() {
const [products, setProducts] = useState([]);
const [selectedProduct, setSelectedProduct] = useState(null);
const [cart, setCart] = useState([]);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
useEffect(() => {
fetch('/api/products')
.then((res) => res.json())
.then((data) => setProducts(data))
.catch((error) => console.error('Error fetching data:', error));
}, []);
const handleProductClick = (productId) => {
fetch(`/api/products/${productId}`)
.then((res) => res.json())
.then((data) => setSelectedProduct(data))
.catch((error) => console.error('Error fetching product details:', error));
};
const handleLogin = () => {
fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
})
.then((res) => res.json())
.then((data) => {
if (data.success) {
setIsLoggedIn(true);
} else {
alert(data.message);
}
})
.catch((error) => console.error('Error logging in:', error));
};
const handleAddToCart = () => {
if (selectedProduct) {
setCart([...cart, selectedProduct]);
}
};
return (
<div>
<h1>E-commerce Product Listing</h1>
{/* Product Listing */}
<ul>
{products.map((product) => (
<li key={product._id} onClick={() => handleProductClick(product._id)}>
<h3>{product.name}</h3>
<p>${product.price}</p>
</li>
))}
</ul>
{/* Product Details */}
{selectedProduct && (
<div>
<h2>{selectedProduct.name}</h2>
<p>{selectedProduct.description}</p>
<p>${selectedProduct.price}</p>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
)}
{/* Cart */}
<h2>Shopping Cart</h2>
<ul>
{cart.map((item, index) => (
<li key={index}>
<h3>{item.name}</h3>
<p>${item.price}</p>
</li>
))}
</ul>
{/* Authentication */}
{!isLoggedIn && (
<div>
<h2>Login</h2>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
</div>
)}
</div>
);
}
export default App;
In this example, I added a product details section, a simple login form, and a basic cart. Remember that this authentication example is basic and not safe. In a real-world setting, you should use secure authentication methods and think about adding user registration, JWT (JSON Web Token) for token-based authentication, and secure password storage.
Implementing user registration, JWT (JSON Web Token) for token-based authentication, and secure password storage
Setting up user registration, JWT for token-based authentication, and secure password storage needs a more detailed approach. Here's an expanded version of the backend (Node.js/Express) code with these features. I'll also update the React frontend to match.
Backend (Node.js/Express):
Update the server.js
file in the e-commerce-server
directory.
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const app = express();
const PORT = process.env.PORT || 5000;
const JWT_SECRET = 'your-secret-key'; // Change this to a long, random string in production
app.use(cors());
app.use(express.json());
mongoose.connect('mongodb://localhost:27017/e-commerce', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const userSchema = new mongoose.Schema({
username: String,
password: String,
});
const User = mongoose.model('User', userSchema);
const productSchema = new mongoose.Schema({
name: String,
price: Number,
description: String,
});
const Product = mongoose.model('Product', productSchema);
// Middleware to verify JWT token
const authenticateToken = (req, res, next) => {
const token = req.header('Authorization');
if (!token) return res.status(401).json({ error: 'Access denied' });
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ error: 'Invalid token' });
req.user = user;
next();
});
};
app.post('/api/register', async (req, res) => {
try {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ username, password: hashedPassword });
await user.save();
res.status(201).json({ message: 'User registered successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ username: user.username }, JWT_SECRET);
res.json({ token });
} catch (error) {
res.status(500).json({ error: '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({ error: 'Internal Server Error' });
}
});
app.get('/api/products/:id', async (req, res) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json(product);
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Protected route (requires authentication)
app.get('/api/user/profile', authenticateToken, (req, res) => {
res.json({ username: req.user.username });
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
In this updated backend code:
Added user registration and login routes.
Passwords are hashed with bcrypt before saving to the database.
JWT is used for token-based authentication.
A protected route
/api/user/profile
is created, needing a valid JWT token to access.
Frontend (React):
Update the src/App.js
file in the e-commerce-client
directory:
// ... (previous code)
const App = () => {
const [products, setProducts] = useState([]);
const [selectedProduct, setSelectedProduct] = useState(null);
const [cart, setCart] = useState([]);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [token, setToken] = useState('');
useEffect(() => {
fetch('/api/products')
.then((res) => res.json())
.then((data) => setProducts(data))
.catch((error) => console.error('Error fetching data:', error));
}, []);
// ... (previous code)
const handleRegister = () => {
fetch('/api/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
})
.then((res) => res.json())
.then((data) => alert(data.message))
.catch((error) => console.error('Error registering:', error));
};
const handleLogin = () => {
fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
})
.then((res) => res.json())
.then((data) => {
if (data.token) {
setToken(data.token);
setIsLoggedIn(true);
} else {
alert('Invalid credentials');
}
})
.catch((error) => console.error('Error logging in:', error));
};
const handleAddToCart = () => {
if (selectedProduct) {
setCart([...cart, selectedProduct]);
}
};
const handleProfile = () => {
fetch('/api/user/profile', {
headers: {
Authorization: token ? `Bearer ${token}` : '',
},
})
.then((res) => res.json())
.then((data) => {
alert(`Logged in as: ${data.username}`);
})
.catch((error) => console.error('Error fetching user profile:', error));
};
// ... (previous code)
return (
<div>
{/* ... (previous code) */}
{/* Authentication */}
{!isLoggedIn ? (
<div>
<h2>Register</h2>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleRegister}>Register</button>
<h2>Login</h2>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e)
=> setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
</div>
) : (
<div>
<button onClick={handleProfile}>View Profile</button>
{/* ... (previous code) */}
</div>
)}
</div>
);
};
export default App;
In this updated frontend code:
In the updated frontend code:
There are forms for user registration and login.
When a user logs in successfully, the JWT token is saved, and the user is logged in.
A "View Profile" button is added to show how to access the protected route
/api/user/profile
using the JWT token.
Remember, this is just a simple example. In a real-world situation, you'd need to add more security steps, like using HTTPS, protecting the JWT secret, and managing token expiration and refresh.