In this section, we will build a simple e-commerce API using Node.js and Express.js. This API will allow users to perform CRUD (Create, Read, Update, Delete) operations on products and manage user authentication.

Objectives

  • Set up a basic Express.js application.
  • Implement CRUD operations for products.
  • Implement user authentication using JWT.
  • Handle errors and validate data.

Prerequisites

  • Basic knowledge of Node.js and Express.js.
  • Understanding of MongoDB and Mongoose.
  • Familiarity with JWT for authentication.

Step-by-Step Guide

  1. Setting Up the Project

1.1 Initialize the Project

First, create a new directory for your project and initialize it with npm.

mkdir ecommerce-api
cd ecommerce-api
npm init -y

1.2 Install Dependencies

Install the necessary dependencies.

npm install express mongoose bcryptjs jsonwebtoken dotenv
npm install --save-dev nodemon

1.3 Project Structure

Create the following directory structure:

ecommerce-api/
├── config/
│   └── db.js
├── controllers/
│   ├── authController.js
│   └── productController.js
├── models/
│   ├── product.js
│   └── user.js
├── routes/
│   ├── authRoutes.js
│   └── productRoutes.js
├── middleware/
│   └── authMiddleware.js
├── .env
├── app.js
└── server.js

  1. Configuring the Database

2.1 Database Configuration

Create a config/db.js file to set up the MongoDB connection.

// config/db.js
const mongoose = require('mongoose');
const dotenv = require('dotenv');

dotenv.config();

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGO_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB connected');
  } catch (error) {
    console.error('MongoDB connection failed:', error.message);
    process.exit(1);
  }
};

module.exports = connectDB;

2.2 Environment Variables

Create a .env file to store environment variables.

PORT=5000
MONGO_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret

  1. Creating Models

3.1 User Model

Create a models/user.js file for the User model.

// 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,
  },
});

userSchema.pre('save', async function (next) {
  if (!this.isModified('password')) {
    next();
  }
  const salt = await bcrypt.genSalt(10);
  this.password = await bcrypt.hash(this.password, salt);
});

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

const User = mongoose.model('User', userSchema);

module.exports = User;

3.2 Product Model

Create a models/product.js file for the Product model.

// models/product.js
const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    required: true,
  },
  price: {
    type: Number,
    required: true,
  },
  countInStock: {
    type: Number,
    required: true,
  },
});

const Product = mongoose.model('Product', productSchema);

module.exports = Product;

  1. Creating Controllers

4.1 Auth Controller

Create a controllers/authController.js file for user authentication.

// controllers/authController.js
const User = require('../models/user');
const jwt = require('jsonwebtoken');
const dotenv = require('dotenv');

dotenv.config();

const generateToken = (id) => {
  return jwt.sign({ id }, process.env.JWT_SECRET, {
    expiresIn: '30d',
  });
};

exports.registerUser = 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: user.name,
      email: user.email,
      token: generateToken(user._id),
    });
  } else {
    res.status(400).json({ message: 'Invalid user data' });
  }
};

exports.authUser = 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' });
  }
};

4.2 Product Controller

Create a controllers/productController.js file for product operations.

// controllers/productController.js
const Product = require('../models/product');

exports.getProducts = async (req, res) => {
  const products = await Product.find({});
  res.json(products);
};

exports.getProductById = async (req, res) => {
  const product = await Product.findById(req.params.id);

  if (product) {
    res.json(product);
  } else {
    res.status(404).json({ message: 'Product not found' });
  }
};

exports.createProduct = async (req, res) => {
  const { name, description, price, countInStock } = req.body;

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

  const createdProduct = await product.save();
  res.status(201).json(createdProduct);
};

exports.updateProduct = async (req, res) => {
  const { name, description, price, countInStock } = req.body;

  const product = await Product.findById(req.params.id);

  if (product) {
    product.name = name;
    product.description = description;
    product.price = price;
    product.countInStock = countInStock;

    const updatedProduct = await product.save();
    res.json(updatedProduct);
  } else {
    res.status(404).json({ message: 'Product not found' });
  }
};

exports.deleteProduct = async (req, res) => {
  const product = await Product.findById(req.params.id);

  if (product) {
    await product.remove();
    res.json({ message: 'Product removed' });
  } else {
    res.status(404).json({ message: 'Product not found' });
  }
};

  1. Creating Routes

5.1 Auth Routes

Create a routes/authRoutes.js file for authentication routes.

// routes/authRoutes.js
const express = require('express');
const { registerUser, authUser } = require('../controllers/authController');

const router = express.Router();

router.post('/register', registerUser);
router.post('/login', authUser);

module.exports = router;

5.2 Product Routes

Create a routes/productRoutes.js file for product routes.

// routes/productRoutes.js
const express = require('express');
const {
  getProducts,
  getProductById,
  createProduct,
  updateProduct,
  deleteProduct,
} = require('../controllers/productController');
const { protect } = require('../middleware/authMiddleware');

const router = express.Router();

router.route('/').get(getProducts).post(protect, createProduct);
router
  .route('/:id')
  .get(getProductById)
  .put(protect, updateProduct)
  .delete(protect, deleteProduct);

module.exports = router;

  1. Middleware

6.1 Auth Middleware

Create a middleware/authMiddleware.js file for authentication middleware.

// middleware/authMiddleware.js
const jwt = require('jsonwebtoken');
const User = require('../models/user');
const dotenv = require('dotenv');

dotenv.config();

exports.protect = async (req, res, next) => {
  let token;

  if (
    req.headers.authorization &&
    req.headers.authorization.startsWith('Bearer')
  ) {
    try {
      token = req.headers.authorization.split(' ')[1];
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      req.user = await User.findById(decoded.id).select('-password');
      next();
    } catch (error) {
      res.status(401).json({ message: 'Not authorized, token failed' });
    }
  }

  if (!token) {
    res.status(401).json({ message: 'Not authorized, no token' });
  }
};

  1. Setting Up the Server

7.1 App Configuration

Create an app.js file to configure the Express application.

// app.js
const express = require('express');
const connectDB = require('./config/db');
const authRoutes = require('./routes/authRoutes');
const productRoutes = require('./routes/productRoutes');
const dotenv = require('dotenv');

dotenv.config();

connectDB();

const app = express();

app.use(express.json());

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

app.use((err, req, res, next) => {
  res.status(500).json({ message: err.message });
});

module.exports = app;

7.2 Starting the Server

Create a server.js file to start the server.

// server.js
const app = require('./app');
const dotenv = require('dotenv');

dotenv.config();

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

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

  1. Running the Application

8.1 Start the Server

Use nodemon to start the server.

npx nodemon server.js

  1. Testing the API

9.1 Register a User

Send a POST request to /api/auth/register with the following JSON body:

{
  "name": "John Doe",
  "email": "[email protected]",
  "password": "password123"
}

9.2 Login a User

Send a POST request to /api/auth/login with the following JSON body:

{
  "email": "[email protected]",
  "password": "password123"
}

9.3 Create a Product

Send a POST request to /api/products with the following JSON body and the token in the Authorization header:

{
  "name": "Sample Product",
  "description": "This is a sample product",
  "price": 100,
  "countInStock": 10
}

9.4 Get All Products

Send a GET request to /api/products.

9.5 Get a Product by ID

Send a GET request to /api/products/:id.

9.6 Update a Product

Send a PUT request to /api/products/:id with the updated product data and the token in the Authorization header.

9.7 Delete a Product

Send a DELETE request to /api/products/:id with the token in the Authorization header.

Conclusion

In this section, we have built a simple e-commerce API using Node.js and Express.js. We covered setting up the project, creating models, controllers, and routes, implementing user authentication, and handling CRUD operations for products. This API can be further extended with more features such as order management, payment integration, and more.

Node.js Course

Module 1: Introduction to Node.js

Module 2: Core Concepts

Module 3: File System and I/O

Module 4: HTTP and Web Servers

Module 5: NPM and Package Management

Module 6: Express.js Framework

Module 7: Databases and ORMs

Module 8: Authentication and Authorization

Module 9: Testing and Debugging

Module 10: Advanced Topics

Module 11: Deployment and DevOps

Module 12: Real-World Projects

© Copyright 2024. All rights reserved