MERN Stack Application Of product Listing in an Ecommerce Application

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-serverdirectory.

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.