Building an e-commerce site using the MERN stack (MongoDB, Express.js, React, and Node.js) is a powerful way to create a full-stack application that can handle everything from user authentication to payment processing. This tutorial will guide you through the process of developing a fully functional e-commerce website from scratch, covering each step in detail. By the end of this tutorial, you will have a comprehensive understanding of how to leverage the MERN stack to build a modern web application.

Introduction to the MERN Stack

The MERN stack is a popular technology stack for building web applications. It consists of four main technologies:

  • MongoDB: A NoSQL database that stores data in a flexible, JSON-like format.
  • Express.js: A web application framework for Node.js that simplifies building server-side applications.
  • React: A front-end JavaScript library for building user interfaces, particularly single-page applications.
  • Node.js: A JavaScript runtime that allows you to run JavaScript on the server side.

Together, these technologies provide a robust foundation for building scalable and maintainable web applications.

Prerequisites

Before we begin, ensure you have the following installed on your machine:

  1. Node.js and npm: Node.js is required to run JavaScript on the server side, and npm (Node Package Manager) is used to manage packages.
  2. MongoDB: You can use a local installation or a cloud service like MongoDB Atlas.
  3. Basic knowledge of JavaScript: Familiarity with ES6 syntax and concepts such as promises and async/await will be helpful.
  4. Understanding of React: Basic knowledge of React components and state management is required.

Setting Up Your Development Environment

Step 1: Create Project Directories

Start by creating your project directory. Open your terminal and run the following commands:

mkdir mern-ecommerce
cd mern-ecommerce
mkdir server client

This will create two main folders: server for your backend code and client for your frontend code.

Step 2: Initialize the Server

Navigate to the server directory and initialize a new Node.js project:

cd server
npm init -y

This command creates a package.json file with default settings.

Step 3: Install Required Packages

Install the necessary packages for your backend:

npm install express mongoose dotenv cors bcryptjs jsonwebtoken
  • express: Framework for building web applications.
  • mongoose: ODM (Object Data Modeling) library for MongoDB.
  • dotenv: Loads environment variables from a .env file.
  • cors: Middleware to enable Cross-Origin Resource Sharing.
  • bcryptjs: Library for hashing passwords.
  • jsonwebtoken: Library for creating and verifying JSON Web Tokens.

Step 4: Set Up Basic Server

Create an index.js file in the server directory:

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();

const app = express();
app.use(cors());
app.use(express.json());

const PORT = process.env.PORT || 5000;

// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('MongoDB Connected'))
    .catch(err => console.log(err));

// Start server
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

In this code snippet:

  • We set up an Express server with CORS enabled and JSON parsing middleware.
  • We connect to MongoDB using Mongoose.

Step 5: Create Environment Variables

Create a .env file in the server directory to store sensitive information such as your MongoDB connection string:

MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret_key

Replace your_mongodb_connection_string with your actual MongoDB URI.

Building the Backend API

Step 6: Define Product Model

Create a new folder named models in the server directory, then create a file named Product.js:

const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
    name: { type: String, required: true },
    price: { type: Number, required: true },
    imageUrl: { type: String, required: true },
    description: { type: String, required: true },
    rating: { type: Number, default: 0 },
    numReviews: { type: Number, default: 0 },
}, {
    timestamps: true,
});

module.exports = mongoose.model('Product', productSchema);

This schema defines the structure of our product data in MongoDB.

Step 7: Create Product Routes

Create a new folder named routes in the server directory and add a file named productRoutes.js:

const express = require('express');
const Product = require('../models/Product');

const router = express.Router();

// @desc   Fetch all products
// @route  GET /api/products
router.get('/', async (req, res) => {
    try {
        const products = await Product.find({});
        res.json(products);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

// @desc   Create a product
// @route  POST /api/products
router.post('/', async (req, res) => {
    const { name, price, imageUrl, description } = req.body;

    const product = new Product({
        name,
        price,
        imageUrl,
        description,
    });

    try {
        const createdProduct = await product.save();
        res.status(201).json(createdProduct);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});

module.exports = router;

In this file:

  • We define routes for fetching all products and creating new products.

Step 8: Integrate Product Routes into Server

Update your index.js to include product routes:

const productRoutes = require('./routes/productRoutes');

app.use('/api/products', productRoutes);

Now your server can handle requests related to products.

Setting Up the Frontend with React

Step 9: Initialize React App

Navigate to the client directory and create a new React application using Create React App:

cd ../client
npx create-react-app .

Step 10: Install Axios

Axios is a promise-based HTTP client that we will use to make API requests:

npm install axios react-router-dom

Step 11: Set Up Basic Routing

Open src/App.js and set up basic routing:

import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import HomeScreen from './screens/HomeScreen';

function App() {
    return (
        <Router>
            <div>
                <Route path="/" component={HomeScreen} exact />
            </div>
        </Router>
    );
}

export default App;

Step 12: Create Home Screen Component

Create a new folder named screens, then create a file named HomeScreen.js:

import React, { useEffect, useState } from 'react';
import axios from 'axios';

function HomeScreen() {
    const [products, setProducts] = useState([]);

    useEffect(() => {
        const fetchProducts = async () => {
            const { data } = await axios.get('/api/products');
            setProducts(data);
        };

        fetchProducts();
    }, []);

    return (
        <div>
            <h1>Products</h1>
            <div className="products">
                {products.map(product => (
                    <div key={product._id}>
                        <h2>{product.name}</h2>
                        <p>${product.price}</p>
                        <img src={product.imageUrl} alt={product.name} />
                    </div>
                ))}
            </div>
        </div>
    );
}

export default HomeScreen;

In this component:

  • We fetch products from our backend API using Axios when the component mounts.
  • We display each product’s name, price, and image.

Implementing Authentication

Step 13: User Model and Routes

Create a user model in models/User.js:

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
}, {
    timestamps: true,
});

// Password hashing middleware
userSchema.pre('save', async function(next) {
    if (!this.isModified('password')) return next();

    this.password = await bcrypt.hash(this.password, 10);
});

userSchema.methods.matchPassword = async function(enteredPassword) {
    return await bcrypt.compare(enteredPassword, this.password);
};

module.exports = mongoose.model('User', userSchema);

Next, create user routes in routes/userRoutes.js:

const express = require('express');
const User = require('../models/User');
const jwt = require('jsonwebtoken');

const router = express.Router();

// @desc   Register new user
// @route  POST /api/users/register
router.post('/register', async (req, res) => {
    const { name, email, password } = req.body;

    const userExists = await User.findOne({ email });

    if (userExists) {
        return res.status(400).json({ message: 'User already exists' });
    }

    const user = await User.create({ name, email, password });

    if (user) {
        res.status(201).json({
            _id: user._id,
            name,
            email,
            token: generateToken(user._id),
        });
    } else {
        res.status(400).json({ message: 'Invalid user data' });
    }
});

// Generate JWT token function
const generateToken = id => jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d' });

module.exports = router;

Integrate user routes into your server by adding this line in index.js:

const userRoutes = require('./routes/userRoutes');
app.use('/api/users', userRoutes);

Step 14: Implement Login Functionality

Add login functionality by creating another route in routes/userRoutes.js:

// @desc   Authenticate user & get token
// @route  POST /api/users/login
router.post('/login', async (req, res) => {
    const { email, password } = req.body;

    const user = await User.findOne({ email });

    if (user && (await user.matchPassword(password))) {
        res.json({
            _id:user._id,
            name:user.name,
            email:user.email,
            token : generateToken(user._id),
        });
    } else {
        res.status(401).json({ message : 'Invalid email or password' });
    }
});

Creating an Admin Dashboard

Step 15: Admin Routes for Product Management

Create admin routes in routes/adminRoutes.js. This will include functionality for adding products and managing existing ones.

const express = require('express');
const Product = require('../models/Product');

const router = express.Router();

// @desc   Add new product as admin 
// @route  POST /api/admin/products 
router.post('/products', async (req,res) => {
   const {name , price , imageUrl , description} = req.body;

   const product= new Product({
       name ,
       price ,
       imageUrl ,
       description 
   });

   try{
       const createdProduct= await product.save();
       res.status(201).json(createdProduct);
   }catch(error){
       res.status(400).json({message:error.message});
   }
});

module.exports= router;

Integrate admin routes into your server by adding this line in index.js:

const adminRoutes= require('./routes/adminRoutes');
app.use('/api/admin', adminRoutes);

Finalizing Your Application

Step 16: Implement Payment Processing

Integrate payment processing using libraries like Stripe or PayPal. In this example, we’ll use Stripe. Install Stripe on both frontend and backend:

For backend:

npm install stripe

For frontend:

npm install @stripe/react-stripe-js @stripe/stripe-js 

Set up payment routes on your backend similar to how you set up other routes.

Step 17: Deploy Your Application

Once you have completed development and testing locally:

  1. Deploy your backend using services like Heroku or DigitalOcean.
  2. Deploy your frontend using services like Vercel or Netlify.

Ensure that you update environment variables accordingly in your deployment settings.

Conclusion

Congratulations! You have successfully built an e-commerce site using the MERN stack. This tutorial covered setting up both backend and frontend components while implementing essential features such as product management and authentication. With these skills under your belt, you can expand upon this foundation by adding more functionalities like reviews and ratings or enhancing UI components with libraries like Material UI or Bootstrap. The possibilities are endless!