Skip to content
BackendDatabaseExpressJsMongoDBNodeJs

7. Express MongoDB CRUD – Complete REST API Example

April 29, 2026 20 min read

नमस्ते दोस्तों! 🙏
स्वागत है The Easy Master पर!

क्या तुमने कभी सोचा है – Express.js and MongoDB se complete CRUD API कैसे बनाएं? Create, Read, Update, Delete – ye sab database ke saath कैसे implement करें?

MongoDB एक NoSQL database है and Express ke saath perfect pair बनता है। Mongoose ODM use करके database operations bohot easy हो जाते हैं।

Express MongoDB CRUD REST API Hindi में समझना बहुत जरूरी है क्योंकि:

  • MERN stack ka core part है
  • Every real-world app mein CRUD operations होते हैं
  • Interview mein pakka CRUD API questions puche jayenge
  • Production-ready API banane ka तरीका सीखोगे

आज क्या सीखोगे?

Topicक्या सीखेगा?
MongoDB SetupAtlas ya local connection
Mongoose SetupSchema and Model
Create (POST)Database mein data insert
Read (GET)Data fetch (single + all)
Update (PUT/PATCH)Data modify
Delete (DELETE)Data remove
FilteringQuery parameters
PaginationLimit, skip, sort
Complete APIProduction-ready example

Kya tumhe pata hai?
Mongoose schema validation automatically handle करता है – tumhe manual validation nahi karni padti!

तो चलिए शुरू करते हैं – Express MongoDB CRUD REST API Hindi सीखने का सफर! 🚀

1. MongoDB & Mongoose – Introduction

MongoDB एक NoSQL document database है – data JSON-like documents में store होता है।

Mongoose एक ODM (Object Data Modeling) library है MongoDB के लिए – schema validation, relationships, queries को easy बनाता है।

MongoDB Document Example:

Code
{
  "_id": "65f1a2b3c4d5e6f7g8h9i0j1",
  "name": "Laptop",
  "price": 50000,
  "category": "electronics",
  "inStock": true,
  "createdAt": "2024-01-15T10:30:00.000Z"
}

Why Mongoose?

FeatureBenefit
SchemaDefine data structure
ValidationAuto data validation
Query BuildingEasy MongoDB queries
MiddlewarePre/post hooks
RelationshipsPopulate references
Type CastingAutomatic type conversion

Express MongoDB CRUD REST API Hindi में हम complete product management API बनाएंगे।

2. Project Setup – Installation

Step 1: Create Project

Code
mkdir express-mongodb-crud
cd express-mongodb-crud
npm init -y

Step 2: Install Dependencies

Code
# Core dependencies
npm install express mongoose dotenv

# Development dependencies
npm install -D nodemon

# Optional (security & utilities)
npm install cors helmet morgan

Step 3: Project Structure

Code
express-mongodb-crud/
├── src/
│   ├── models/
│   │   └── Product.js
│   ├── routes/
│   │   └── products.js
│   ├── controllers/
│   │   └── productController.js
│   ├── middleware/
│   │   └── errorHandler.js
│   ├── config/
│   │   └── database.js
│   └── app.js
├── .env
├── .gitignore
├── package.json
└── server.js

Step 4: package.json Scripts

Code
{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  }
}

Step 5: Environment Variables (.env)

Code
# .env
PORT=5000
NODE_ENV=development
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/productdb?retryWrites=true&w=majority

3. Database Connection – MongoDB Atlas

MongoDB Atlas Setup:

  1. Sign up at mongodb.com/atlas
  2. Create free cluster
  3. Get connection string
  4. Add IP whitelist (0.0.0.0/0 for development)

Database Connection Code:

Code
// src/config/database.js
const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGODB_URI);
    console.log(`✅ MongoDB Connected: ${conn.connection.host}`);
    console.log(`📦 Database: ${conn.connection.name}`);
  } catch (error) {
    console.error(`❌ MongoDB Connection Error: ${error.message}`);
    process.exit(1);
  }
};

module.exports = connectDB;

Main Server File:

Code
// server.js
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const connectDB = require('./src/config/database');
const productRoutes = require('./src/routes/products');
const errorHandler = require('./src/middleware/errorHandler');

// Connect to database
connectDB();

const app = express();
const PORT = process.env.PORT || 5000;

// Middleware
app.use(helmet());
app.use(cors());
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

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

// Health check
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'OK',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    database: mongoose.connection.readyState === 1 ? 'Connected' : 'Disconnected'
  });
});

// 404 handler
app.use('*', (req, res) => {
  res.status(404).json({
    success: false,
    error: `Cannot ${req.method} ${req.originalUrl}`
  });
});

// Global error handler
app.use(errorHandler);

// Start server
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
  console.log(`📝 Environment: ${process.env.NODE_ENV}`);
});

4. Mongoose Schema – Product Model

Product Schema:

Code
// src/models/Product.js
const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Product name is required'],
    trim: true,
    minlength: [3, 'Product name must be at least 3 characters'],
    maxlength: [100, 'Product name cannot exceed 100 characters']
  },
  description: {
    type: String,
    required: [true, 'Product description is required'],
    trim: true,
    minlength: [10, 'Description must be at least 10 characters'],
    maxlength: [1000, 'Description cannot exceed 1000 characters']
  },
  price: {
    type: Number,
    required: [true, 'Product price is required'],
    min: [0, 'Price cannot be negative'],
    max: [10000000, 'Price cannot exceed 10,000,000']
  },
  category: {
    type: String,
    required: [true, 'Product category is required'],
    enum: {
      values: ['electronics', 'clothing', 'books', 'home', 'beauty', 'sports', 'toys', 'other'],
      message: '{VALUE} is not a valid category'
    },
    default: 'other'
  },
  brand: {
    type: String,
    trim: true,
    maxlength: [50, 'Brand name cannot exceed 50 characters']
  },
  stock: {
    type: Number,
    required: [true, 'Stock quantity is required'],
    min: [0, 'Stock cannot be negative'],
    default: 0
  },
  rating: {
    type: Number,
    min: [0, 'Rating cannot be less than 0'],
    max: [5, 'Rating cannot exceed 5'],
    default: 0
  },
  images: [{
    type: String,
    url: String,
    publicId: String
  }],
  isActive: {
    type: Boolean,
    default: true
  },
  tags: [{
    type: String,
    trim: true
  }],
  createdBy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  }
}, {
  timestamps: true // Adds createdAt and updatedAt automatically
});

// Indexes for better query performance
productSchema.index({ name: 'text', description: 'text' });
productSchema.index({ category: 1 });
productSchema.index({ price: 1 });
productSchema.index({ createdAt: -1 });

// Virtual for discounted price
productSchema.virtual('discountedPrice').get(function() {
  if (this.discount) {
    return this.price * (1 - this.discount / 100);
  }
  return this.price;
});

// Middleware: Before saving
productSchema.pre('save', function(next) {
  // Convert name to title case
  if (this.name) {
    this.name = this.name.split(' ')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join(' ');
  }
  next();
});

// Instance method
productSchema.methods.updateStock = async function(quantity) {
  this.stock += quantity;
  return await this.save();
};

// Static method
productSchema.statics.findByCategory = function(category) {
  return this.find({ category, isActive: true });
};

// To JSON transform (remove __v, etc.)
productSchema.set('toJSON', {
  transform: (doc, ret) => {
    ret.id = ret._id;
    delete ret._id;
    delete ret.__v;
    return ret;
  }
});

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

module.exports = Product;

5. Create (POST) – Product Create API

Product Controller:

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

// @desc    Create a new product
// @route   POST /api/products
// @access  Public (or Private with auth)
const createProduct = async (req, res, next) => {
  try {
    const { name, description, price, category, brand, stock, tags } = req.body;
    
    // Check if product with same name exists
    const existingProduct = await Product.findOne({ name });
    if (existingProduct) {
      return res.status(409).json({
        success: false,
        error: `Product with name "${name}" already exists`
      });
    }
    
    const product = await Product.create({
      name,
      description,
      price,
      category,
      brand,
      stock: stock || 0,
      tags: tags || []
    });
    
    res.status(201).json({
      success: true,
      message: 'Product created successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Create multiple products (bulk)
// @route   POST /api/products/bulk
// @access  Public
const createBulkProducts = async (req, res, next) => {
  try {
    const products = req.body.products;
    
    if (!Array.isArray(products) || products.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'Products array is required'
      });
    }
    
    const createdProducts = await Product.insertMany(products);
    
    res.status(201).json({
      success: true,
      message: `${createdProducts.length} products created successfully`,
      data: createdProducts
    });
  } catch (error) {
    next(error);
  }
};

module.exports = {
  createProduct,
  createBulkProducts
};

Product Routes:

Code
// src/routes/products.js
const express = require('express');
const router = express.Router();
const {
  createProduct,
  createBulkProducts
} = require('../controllers/productController');

// POST /api/products - Create single product
router.post('/', createProduct);

// POST /api/products/bulk - Create multiple products
router.post('/bulk', createBulkProducts);

module.exports = router;

6. Read (GET) – Get Products API

Get All Products with Filtering:

Code
// src/controllers/productController.js (continued)

// @desc    Get all products with filtering, sorting, pagination
// @route   GET /api/products
// @access  Public
const getAllProducts = async (req, res, next) => {
  try {
    const {
      category,
      minPrice,
      maxPrice,
      brand,
      inStock,
      minRating,
      search,
      sortBy = 'createdAt',
      order = 'desc',
      page = 1,
      limit = 10
    } = req.query;
    
    // Build filter object
    const filter = {};
    
    if (category) {
      filter.category = category;
    }
    
    if (brand) {
      filter.brand = brand;
    }
    
    if (minPrice || maxPrice) {
      filter.price = {};
      if (minPrice) filter.price.$gte = Number(minPrice);
      if (maxPrice) filter.price.$lte = Number(maxPrice);
    }
    
    if (inStock === 'true') {
      filter.stock = { $gt: 0 };
    } else if (inStock === 'false') {
      filter.stock = 0;
    }
    
    if (minRating) {
      filter.rating = { $gte: Number(minRating) };
    }
    
    if (search) {
      filter.$or = [
        { name: { $regex: search, $options: 'i' } },
        { description: { $regex: search, $options: 'i' } },
        { tags: { $in: [new RegExp(search, 'i')] } }
      ];
    }
    
    // Pagination
    const pageNum = parseInt(page);
    const limitNum = parseInt(limit);
    const skip = (pageNum - 1) * limitNum;
    
    // Sorting
    const sort = {};
    sort[sortBy] = order === 'desc' ? -1 : 1;
    
    // Execute query
    const [products, total] = await Promise.all([
      Product.find(filter)
        .sort(sort)
        .skip(skip)
        .limit(limitNum)
        .select('-__v'),
      Product.countDocuments(filter)
    ]);
    
    res.status(200).json({
      success: true,
      count: products.length,
      total,
      pagination: {
        page: pageNum,
        limit: limitNum,
        totalPages: Math.ceil(total / limitNum),
        hasNext: pageNum * limitNum < total,
        hasPrev: pageNum > 1
      },
      data: products
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Get product by ID
// @route   GET /api/products/:id
// @access  Public
const getProductById = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    // Check if ID is valid MongoDB ObjectId
    if (!mongoose.Types.ObjectId.isValid(id)) {
      return res.status(400).json({
        success: false,
        error: 'Invalid product ID format'
      });
    }
    
    const product = await Product.findById(id).select('-__v');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    res.status(200).json({
      success: true,
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Get products by category
// @route   GET /api/products/category/:category
// @access  Public
const getProductsByCategory = async (req, res, next) => {
  try {
    const { category } = req.params;
    const { limit = 20 } = req.query;
    
    const products = await Product.findByCategory(category)
      .limit(parseInt(limit))
      .select('-__v');
    
    res.status(200).json({
      success: true,
      count: products.length,
      category,
      data: products
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Get all unique categories
// @route   GET /api/products/categories/all
// @access  Public
const getCategories = async (req, res, next) => {
  try {
    const categories = await Product.distinct('category');
    
    res.status(200).json({
      success: true,
      count: categories.length,
      data: categories
    });
  } catch (error) {
    next(error);
  }
};

module.exports = {
  createProduct,
  createBulkProducts,
  getAllProducts,
  getProductById,
  getProductsByCategory,
  getCategories
};

Update Routes:

Code
// src/routes/products.js (updated)
const express = require('express');
const router = express.Router();
const {
  createProduct,
  createBulkProducts,
  getAllProducts,
  getProductById,
  getProductsByCategory,
  getCategories
} = require('../controllers/productController');

// GET routes
router.get('/', getAllProducts);
router.get('/categories', getCategories);
router.get('/category/:category', getProductsByCategory);
router.get('/:id', getProductById);

// POST routes
router.post('/', createProduct);
router.post('/bulk', createBulkProducts);

module.exports = router;

7. Read Single (GET) – Get Product by ID

(Already covered in section above)

8. Update (PUT/PATCH) – Update Product

Update Controllers:

Code
// src/controllers/productController.js (continued)

// @desc    Update product (full update - PUT)
// @route   PUT /api/products/:id
// @access  Public
const updateProductFull = async (req, res, next) => {
  try {
    const { id } = req.params;
    const { name, description, price, category, brand, stock, rating, tags } = req.body;
    
    // Check if ID is valid
    if (!mongoose.Types.ObjectId.isValid(id)) {
      return res.status(400).json({
        success: false,
        error: 'Invalid product ID format'
      });
    }
    
    // Full update requires all fields
    if (!name || !description || !price || !category) {
      return res.status(400).json({
        success: false,
        error: 'Name, description, price, and category are required for full update'
      });
    }
    
    const product = await Product.findByIdAndUpdate(
      id,
      {
        name,
        description,
        price,
        category,
        brand,
        stock: stock || 0,
        rating: rating || 0,
        tags: tags || []
      },
      {
        new: true,      // Return updated document
        runValidators: true,  // Run schema validation
        context: 'query'
      }
    ).select('-__v');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    res.status(200).json({
      success: true,
      message: 'Product updated successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Update product (partial update - PATCH)
// @route   PATCH /api/products/:id
// @access  Public
const updateProductPartial = async (req, res, next) => {
  try {
    const { id } = req.params;
    const updates = req.body;
    
    // Check if ID is valid
    if (!mongoose.Types.ObjectId.isValid(id)) {
      return res.status(400).json({
        success: false,
        error: 'Invalid product ID format'
      });
    }
    
    // Remove fields that shouldn't be updated
    delete updates._id;
    delete updates.__v;
    delete updates.createdAt;
    
    const product = await Product.findByIdAndUpdate(
      id,
      updates,
      {
        new: true,
        runValidators: true,
        context: 'query'
      }
    ).select('-__v');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    res.status(200).json({
      success: true,
      message: 'Product updated successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Update product stock
// @route   PATCH /api/products/:id/stock
// @access  Public
const updateStock = async (req, res, next) => {
  try {
    const { id } = req.params;
    const { quantity } = req.body;
    
    if (quantity === undefined) {
      return res.status(400).json({
        success: false,
        error: 'Quantity is required'
      });
    }
    
    const product = await Product.findById(id);
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    await product.updateStock(quantity);
    
    res.status(200).json({
      success: true,
      message: 'Stock updated successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

module.exports = {
  // ... previous exports
  updateProductFull,
  updateProductPartial,
  updateStock
};

Update Routes:

Code
// src/routes/products.js (updated)
const express = require('express');
const router = express.Router();
const {
  createProduct,
  createBulkProducts,
  getAllProducts,
  getProductById,
  getProductsByCategory,
  getCategories,
  updateProductFull,
  updateProductPartial,
  updateStock
} = require('../controllers/productController');

// GET routes
router.get('/', getAllProducts);
router.get('/categories', getCategories);
router.get('/category/:category', getProductsByCategory);
router.get('/:id', getProductById);

// POST routes
router.post('/', createProduct);
router.post('/bulk', createBulkProducts);

// PUT routes (full update)
router.put('/:id', updateProductFull);

// PATCH routes (partial update)
router.patch('/:id', updateProductPartial);
router.patch('/:id/stock', updateStock);

module.exports = router;

9. Delete (DELETE) – Remove Product

Delete Controllers:

Code
// src/controllers/productController.js (continued)

// @desc    Delete product (hard delete)
// @route   DELETE /api/products/:id
// @access  Public
const deleteProduct = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    // Check if ID is valid
    if (!mongoose.Types.ObjectId.isValid(id)) {
      return res.status(400).json({
        success: false,
        error: 'Invalid product ID format'
      });
    }
    
    const product = await Product.findByIdAndDelete(id);
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    res.status(200).json({
      success: true,
      message: 'Product deleted successfully',
      data: {
        id: product._id,
        name: product.name
      }
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Delete multiple products (bulk)
// @route   DELETE /api/products/bulk
// @access  Public
const deleteBulkProducts = async (req, res, next) => {
  try {
    const { ids } = req.body;
    
    if (!Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'Products IDs array is required'
      });
    }
    
    const result = await Product.deleteMany({ _id: { $in: ids } });
    
    res.status(200).json({
      success: true,
      message: `${result.deletedCount} products deleted successfully`,
      data: {
        deletedCount: result.deletedCount
      }
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Soft delete (set isActive to false)
// @route   PATCH /api/products/:id/soft-delete
// @access  Public
const softDeleteProduct = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const product = await Product.findByIdAndUpdate(
      id,
      { isActive: false },
      { new: true }
    ).select('-__v');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    res.status(200).json({
      success: true,
      message: 'Product soft deleted successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

// @desc    Restore soft deleted product
// @route   PATCH /api/products/:id/restore
// @access  Public
const restoreProduct = async (req, res, next) => {
  try {
    const { id } = req.params;
    
    const product = await Product.findByIdAndUpdate(
      id,
      { isActive: true },
      { new: true }
    ).select('-__v');
    
    if (!product) {
      return res.status(404).json({
        success: false,
        error: `Product with ID ${id} not found`
      });
    }
    
    res.status(200).json({
      success: true,
      message: 'Product restored successfully',
      data: product
    });
  } catch (error) {
    next(error);
  }
};

module.exports = {
  // ... previous exports
  deleteProduct,
  deleteBulkProducts,
  softDeleteProduct,
  restoreProduct
};

Complete Routes:

Code
// src/routes/products.js (complete)
const express = require('express');
const router = express.Router();
const {
  // Create
  createProduct,
  createBulkProducts,
  
  // Read
  getAllProducts,
  getProductById,
  getProductsByCategory,
  getCategories,
  
  // Update
  updateProductFull,
  updateProductPartial,
  updateStock,
  
  // Delete
  deleteProduct,
  deleteBulkProducts,
  softDeleteProduct,
  restoreProduct
} = require('../controllers/productController');

// ============ GET Routes ============
router.get('/', getAllProducts);
router.get('/categories', getCategories);
router.get('/category/:category', getProductsByCategory);
router.get('/:id', getProductById);

// ============ POST Routes ============
router.post('/', createProduct);
router.post('/bulk', createBulkProducts);

// ============ PUT Routes ============
router.put('/:id', updateProductFull);

// ============ PATCH Routes ============
router.patch('/:id', updateProductPartial);
router.patch('/:id/stock', updateStock);
router.patch('/:id/soft-delete', softDeleteProduct);
router.patch('/:id/restore', restoreProduct);

// ============ DELETE Routes ============
router.delete('/:id', deleteProduct);
router.delete('/bulk/delete', deleteBulkProducts);

module.exports = router;

10. Filtering & Pagination

Advanced Query Features:

Code
// src/controllers/productController.js - Enhanced getAllProducts

const getAllProductsAdvanced = async (req, res, next) => {
  try {
    // 1. Filtering
    const queryObj = { ...req.query };
    const excludedFields = ['page', 'sort', 'limit', 'fields', 'search'];
    excludedFields.forEach(field => delete queryObj[field]);
    
    // 2. Advanced filtering (gte, gt, lte, lt)
    let queryStr = JSON.stringify(queryObj);
    queryStr = queryStr.replace(/\b(gte|gt|lte|lt)\b/g, match => `$${match}`);
    
    let query = Product.find(JSON.parse(queryStr));
    
    // 3. Search (text search)
    if (req.query.search) {
      query = query.find({
        $text: { $search: req.query.search }
      });
    }
    
    // 4. Sorting
    if (req.query.sort) {
      const sortBy = req.query.sort.split(',').join(' ');
      query = query.sort(sortBy);
    } else {
      query = query.sort('-createdAt');
    }
    
    // 5. Field limiting
    if (req.query.fields) {
      const fields = req.query.fields.split(',').join(' ');
      query = query.select(fields);
    } else {
      query = query.select('-__v');
    }
    
    // 6. Pagination
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;
    const skip = (page - 1) * limit;
    
    query = query.skip(skip).limit(limit);
    
    // Execute query
    const products = await query;
    const total = await Product.countDocuments(JSON.parse(queryStr));
    
    res.status(200).json({
      success: true,
      count: products.length,
      total,
      pagination: {
        page,
        limit,
        totalPages: Math.ceil(total / limit),
        hasNext: page * limit < total,
        hasPrev: page > 1
      },
      data: products
    });
  } catch (error) {
    next(error);
  }
};

11. Complete Product API

Error Handler Middleware:

Code
// src/middleware/errorHandler.js
const mongoose = require('mongoose');

const errorHandler = (err, req, res, next) => {
  let error = { ...err };
  error.message = err.message;
  
  // Log error
  console.error('Error:', err);
  
  // Mongoose bad ObjectId
  if (err.name === 'CastError') {
    const message = `Resource not found with id of ${err.value}`;
    error = { message, statusCode: 404 };
  }
  
  // Mongoose duplicate key
  if (err.code === 11000) {
    const field = Object.keys(err.keyPattern)[0];
    const message = `Duplicate field value: ${field}. Please use another value`;
    error = { message, statusCode: 409 };
  }
  
  // Mongoose validation error
  if (err.name === 'ValidationError') {
    const messages = Object.values(err.errors).map(val => val.message);
    const message = `Invalid input data: ${messages.join('. ')}`;
    error = { message, statusCode: 400 };
  }
  
  res.status(error.statusCode || 500).json({
    success: false,
    error: error.message || 'Server Error',
    stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
  });
};

module.exports = errorHandler;

12. Testing with Postman

API Endpoints:

MethodEndpointDescription
POST/api/productsCreate product
POST/api/products/bulkCreate multiple products
GET/api/productsGet all products
GET/api/products/:idGet product by ID
GET/api/products/category/:categoryGet by category
GET/api/products/categoriesGet all categories
PUT/api/products/:idFull update
PATCH/api/products/:idPartial update
PATCH/api/products/:id/stockUpdate stock
PATCH/api/products/:id/soft-deleteSoft delete
PATCH/api/products/:id/restoreRestore product
DELETE/api/products/:idHard delete
DELETE/api/products/bulk/deleteBulk delete

Test Examples:

Code
# Create product
curl -X POST http://localhost:5000/api/products \
  -H "Content-Type: application/json" \
  -d '{
    "name": "MacBook Pro",
    "description": "Apple M3 chip, 16GB RAM, 512GB SSD",
    "price": 150000,
    "category": "electronics",
    "brand": "Apple",
    "stock": 10
  }'

# Get all products with filters
curl "http://localhost:5000/api/products?category=electronics&minPrice=50000&sortBy=price&order=desc&page=1&limit=5"

# Get product by ID
curl http://localhost:5000/api/products/65f1a2b3c4d5e6f7g8h9i0j1

# Update product
curl -X PATCH http://localhost:5000/api/products/65f1a2b3c4d5e6f7g8h9i0j1 \
  -H "Content-Type: application/json" \
  -d '{"price": 145000, "stock": 8}'

# Delete product
curl -X DELETE http://localhost:5000/api/products/65f1a2b3c4d5e6f7g8h9i0j1

13. Common Mistakes + Solutions

Mistake 1: Not handling MongoDB connection errors

Code
// ❌ No error handling
mongoose.connect(process.env.MONGODB_URI);

// ✅ Handle connection errors
mongoose.connect(process.env.MONGODB_URI)
  .then(() => console.log('Connected'))
  .catch(err => {
    console.error('Connection failed:', err);
    process.exit(1);
  });

Mistake 2: Not validating ObjectId

Code
// ❌ Invalid ID crashes app
const product = await Product.findById(req.params.id);

// ✅ Validate ID first
if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
  return res.status(400).json({ error: 'Invalid ID format' });
}

Mistake 3: Not using lean() for read-only queries

Code
// ❌ Returns Mongoose documents (heavy)
const products = await Product.find();

// ✅ Use lean() for plain objects (faster)
const products = await Product.find().lean();

Mistake 4: Forgetting to handle duplicate keys

Code
// ❌ Duplicate email crashes
await User.create({ email });

// ✅ Handle duplicate error
try {
  await User.create({ email });
} catch (error) {
  if (error.code === 11000) {
    return res.status(409).json({ error: 'Email already exists' });
  }
}

14. Quick Cheat Sheet

Mongoose CRUD Operations:

Code
// CREATE
const product = new Product(data);
await product.save();
// OR
const product = await Product.create(data);

// READ (all)
const products = await Product.find(filter).sort().skip().limit();

// READ (single)
const product = await Product.findById(id);
// OR
const product = await Product.findOne({ name: 'Laptop' });

// UPDATE
const product = await Product.findByIdAndUpdate(id, updates, { new: true });

// DELETE
await Product.findByIdAndDelete(id);
// OR
await Product.deleteMany({ condition });

Common Filters:

Code
// Comparison operators
{ price: { $gte: 100, $lte: 1000 } }
{ age: { $gt: 18 } }
{ name: { $in: ['John', 'Jane'] } }

// Logical operators
{ $or: [{ price: { $lt: 100 } }, { category: 'sale' }] }
{ $and: [{ price: { $gte: 100 } }, { inStock: true }] }

// Text search
{ $text: { $search: 'laptop' } }

// Array operations
{ tags: { $in: ['electronics'] } }

15. FAQ

Q1: Express MongoDB CRUD REST API Hindi में सबसे important kya hai?
Mongoose schema validation – ye database mein invalid data enter होने से रोकता है।

Q2: MongoDB Atlas vs local – kya use karein?
Development – local, Production – Atlas (managed, scalable, backup)

Q3: findByIdAndUpdate vs updateOne – kya antar hai?
findByIdAndUpdate returns updated document, updateOne returns only update result.

Q4: lean() kyun use karein?
Performance के लिए – Mongoose documents के बजाय plain JavaScript objects return करता है।

Q5: Virtual fields kya hote hain?
Schema में define किए गए fields जो database में store नहीं होते – computed properties के लिए।

Q6: Populate kya karta hai?
References को actual documents से replace करता है (joins for MongoDB)।

Q7: Index kyun banayein?
Query performance improve करने के लिए – frequently queried fields पर index banाएं।

Q8: Soft delete क्या होता है?
isActive: false mark करना – data delete नहीं होता, बस hidden हो जाता है।

Q9: Bulk operations kaise perform karein?
insertMany(), updateMany(), deleteMany() – multiple documents पर operation।

Q10: Production mein connection pool size kitna rakhein?
Default 100 है – जरूरत के according adjust करो।

16. Conclusion

बहुत बढ़िया दोस्तों! आज हमने Express MongoDB CRUD REST API Hindi को पूरी detail में समझा।

Quick Recap:

OperationMethodMongoose Method
CreatePOSTcreate() or save()
ReadGETfind(), findById()
UpdatePUT/PATCHfindByIdAndUpdate()
DeleteDELETEfindByIdAndDelete()

Mera personal experience:

MongoDB + Express combination bohot powerful है। Mongoose schema validation se data consistency बनी रहती है। Pagination, filtering, sorting – sab easily implement हो जाता है।

Tum bhi ye steps follow karo:

  1. ✅ MongoDB Atlas account banao
  2. ✅ Mongoose schema define करो
  3. ✅ CRUD API implement करो
  4. ✅ Filtering + pagination add करो
  5. ✅ Error handling add करो

अब तुम्हारी बारी है!

नीचे comment में बताओ:

  1. तुम MongoDB use karoge ya SQL?
  2. कौन सा CRUD operation सबसे easy लगा?
  3. अगला topic क्या चाहिए? (JWT Authentication? File Upload? Socket.io with MongoDB?)

The Easy Master पर बने रहो। Happy Coding! 🚀💻

Resources

Additional Resources

TheEasyMaster

Author at The Easy Master.

Related posts

Leave a Reply

Your email address will not be published. Required fields are marked *