DEV Community

Cover image for How to Create a RESTful API with Node.js, Express, and MongoDB using MVC pattern
M Inam
M Inam

Posted on • Edited on

How to Create a RESTful API with Node.js, Express, and MongoDB using MVC pattern

In this post, we'll explore setting up a basic RESTful API using Node.js, Express.js, MongoDB, and Mongoose using the MVC (Model-View-Controller) pattern. By structuring our application this way, we create a modular, organized, and scalable project.

What is the MVC Pattern?

MVC is an architectural pattern that divides an application into three main interconnected components:

  1. Model: Represents the data and business logic.
  2. View: Handles the user interface and presentation.
  3. Controller: Acts as an intermediary between the Model and View, processing user inputs and updating the Model or View as needed.

This separation helps in managing complex applications, as each component has a distinct role, making the codebase more organized and easier to maintain.

Why Use MVC?

MVC provides several benefits:

  • Separation of Concerns: Each component has a well-defined role, making it easier to manage and modify.

  • Scalability: The structure is modular, allowing teams to work on different parts of the application concurrently.

  • Reusability: The View can be reused for different parts of the application, while the Model can serve different views, making the architecture flexible.

  • Testability: Each component can be tested independently, leading to cleaner and more reliable code.

Project Overview

Our tech stack for this API includes:

  • Node.js: JavaScript runtime for backend development
  • Express.js: Web application framework
  • MongoDB: NoSQL database
  • Mongoose: ODM for MongoDB
  • Nodemon: Automatically restarts the server on file changes

In this setup:

  • Models represent our data and business logic.
  • Controllers handle the main application logic.
  • Routes connect HTTP requests to the controller functions.

1. Project Setup

Start by creating a new project folder and installing the necessary packages.

mkdir MVCPattern
cd MVCPattern
npm init -y
npm install express mongoose dotenv nodemon
Enter fullscreen mode Exit fullscreen mode

2. Environment Variables

Add a .env file in the root directory to store sensitive information:

PORT=3000
MONGODB_URI=<Your MongoDB URI>
Enter fullscreen mode Exit fullscreen mode

3. Setting Up the MVC Structure

Your project structure should look like this:

MVCPattern/
│
├── config/
│   └── db.js        # MongoDB connection
├── controllers/
│   └── productController.js # Business logic for products
├── models/
│   └── productModel.js  # Mongoose schema
├── routes/
│   └── productsRoute.js  # Route handlers for product endpoints
├── .env               # Environment variables
└── index.js           # Main server file
Enter fullscreen mode Exit fullscreen mode

4. Configuring MongoDB Connection

In config/db.js, configure Mongoose to connect to MongoDB using the URI from our .env file.

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

dotenv.config();

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGODB_URI, {
      useNewUrlParser: true,
    });
    console.log(`MongoDB Connected: ${conn.connection.host}`);
  } catch (error) {
    console.error(error.message);
    process.exit(1);
  }
};

module.exports = connectDB;
Enter fullscreen mode Exit fullscreen mode

5. Server Setup in index.js

Our main server file, index.js, sets up Express, connects to MongoDB, and loads the routes.

// index.js
const express = require("express");
const connectDB = require("./config/db");
const dotenv = require("dotenv");
const productsRoute = require("./routes/productsRoute");
const app = express();

dotenv.config();
const port = process.env.PORT;

// Connect to MongoDB
connectDB();

// Middleware
app.use(express.json());

// Routes
app.get("/", (req, res) => {
  res.send("Backend API");
});
app.use("/api", productsRoute);

// Start server
app.listen(port, () => {
  console.log(`Server is running on port: ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

6. Defining the Product Model

The Product Model defines the data structure for products and is stored in models/productModel.js. This schema is managed by Mongoose, allowing us to interact with MongoDB collections as JavaScript objects.

// models/productModel.js
const { Schema, model } = require("mongoose");

const ProductSchema = new Schema({
  name: {
    type: String,
    required: true,
  },
  price: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    required: true,
  },
  category: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

const ProductModel = model("Products", ProductSchema);

module.exports = ProductModel;
Enter fullscreen mode Exit fullscreen mode

7. Creating the Controller for Business Logic

The Controller in controllers/productController.js contains all the business logic for handling CRUD operations. Each function interacts with the model to perform database operations.

// controllers/productController.js
const Product = require("../models/productModel");

const getAllProducts = async (req, res) => {
  try {
    const allProducts = await Product.find();
    res.status(200).json({
      success: true,
      products: allProducts,
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "Internal Server Error...",
    });
  }
};

const createProduct = async (req, res) => {
  try {
    const { name, price, description, category } = req.body;
    const newProduct = new Product({ name, price, description, category });
    await newProduct.save();
    res.status(201).json({
      success: true,
      product: newProduct,
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "Internal Server Error...",
    });
  }
};

const updateProduct = async (req, res) => {
  try {
    const { id } = req.params;
    const { name, price, description, category } = req.body;
    const updatedProduct = await Product.findByIdAndUpdate(
      id,
      { name, price, description, category },
      { new: true }
    );
    res.status(200).json({
      success: true,
      product: updatedProduct,
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "Internal Server Error...",
    });
  }
};

const deleteProduct = async (req, res) => {
  try {
    const { id } = req.params;
    const deletedProduct = await Product.findByIdAndDelete(id);
    if (!deletedProduct) {
      return res.status(404).json({ message: "Product not found." });
    }
    res.status(200).json({
      success: true,
      message: "Product deleted successfully.",
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "Internal Server Error...",
    });
  }
};

module.exports = {
  getAllProducts,
  createProduct,
  updateProduct,
  deleteProduct,
};
Enter fullscreen mode Exit fullscreen mode

8. Setting Up Routes

The Router in routes/productsRoute.js connects each endpoint to its corresponding controller function.

// routes/productsRoute.js
const express = require("express");
const router = express.Router();
const {
  getAllProducts,
  updateProduct,
  createProduct,
  deleteProduct,
} = require("../controllers/productController");

router.get("/products", getAllProducts);
router.post("/products", createProduct);
router.put("/products/:id", updateProduct);
router.delete("/products/:id", deleteProduct);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Testing the API

With this setup, you can test each endpoint using a tool like Postman:

  • GET /api/products - Fetch all products
  • POST /api/products - Create a new product
  • PUT /api/products/:id - Update an existing product by ID
  • DELETE /api/products/:id - Delete a product by ID

Conclusion

In this tutorial, we structured a Node.js application using the MVC pattern with Express, MongoDB, and Mongoose. This pattern organizes code into modular sections, keeping our project maintainable and scalable.

Top comments (1)

Collapse
 
nestor_daza_8624625f4d9d0 profile image
Nestor Daza

Great post! If someone needs a free MongoDB instance to test this out, go to the official Atlas site