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:
- 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.
- MongoDB: You can use a local installation or a cloud service like MongoDB Atlas.
- Basic knowledge of JavaScript: Familiarity with ES6 syntax and concepts such as promises and async/await will be helpful.
- 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:
- Deploy your backend using services like Heroku or DigitalOcean.
- 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!