Understanding MVC Architecture in the MERN Stack

Understanding MVC Architecture in the MERN Stack

Hello readers in this article I am going to explain you the MVC Pattern which we generally use when we are building a mern stack project. Before going to learn how to make MVC Pattern let first understand what is mean by MVC

So in very simple word MVC is a way to organise your code in a specific folder strucher so that it becomes easy to understand the logic of code(or flow of data). In other word you can say a MVC pattern is a fancy word to saying not to write all your backend logic in a single index.js:)

If you are building very small project/beginer level project then its good to keep your number of file to limted but if you want to build a big project then you should always follow this pattern. This help you in many ways for example you can visuliaze your code easily, easy to find and resolve bugs, easy to add new features, maitailibiy and flexitiy of code become easier and many much more.

Let suppose one example which will be use as a refrence in this article→

Suppose on the frontend side(in react) we have a button on which click disply the number of user strore in database so user click on the button an api is likend with this button it will send request to backend (Controller) and query for the data in database(Model) then database send back requied data to controller and it will return to frontend(View)

Overview of MVC pattern

The full form of MVC is Model-View-Controller, and here is a short definition of each:

  • Model: It is the logic written for interaction with the database.

  • View: It represents how the information will be shown to the user (the frontend part).

  • Controller: The actual business logic is written here, which handles requests and sends responses.

Folder Stucher which follow MVC pattern

In this pattern, there are some important folders (backend) that you need to create. Let's understand them one by one:

Models

This folder defines the Mongoose schema and models, which explain how the data interacts with the MongoDB database. For example:

  • user.model.js (describes the user schema)

  • product.model.js (describes the product schema)

Controllers

This folder holds the business logic for handling requests and interacting with the models.

Routes

It defines the API endpoints that handle incoming requests and pass them to the controller. It helps organize the codebase into different parts. For example:

  • user.router.js (contains endpoints related to users)

  • product.router.js (contains endpoints related to products)

Config

This folder contains the configuration setup for the database, where you can add the MongoDB URL for connection.

index.js

This is the entry point for the backend, where you set up the Express server to start your application.

Apart from the above, you can add other folders as well. For example, to handle middleware, you can create a new folder for middleware, and a utils folder for helper methods such as data formatting or error handling. You need one .env file for storing important credentials.

Now, let's explore the Model, View, and Controller in detail, one by one.

Model

To make a full-stack project, you need a database for various purposes, such as keeping user data safe, showing the UI based on information saved in the database, and so on. So, it is very important to write code for database logic in a very efficient manner, and for this, we keep some rules while working with the database.

So, for that, we need two folders:

  1. Models → Where we define the schema.

  2. Config → Where we add the logic for adding the MongoDB URL connection string.

Below is the sample code for connetion string of monogoDB

import mongoose from "mongoose";
import dotenv from "dotenv";

dotenv.config(); 

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

export default connectDB;

In the above code, we first import Mongoose (Object Data Modeling for MongoDB in Node.js) and dotenv (for storing the MongoDB credentials so that they do not get exposed globally). Then, we define the function for connection and make sure to use async/await (connection with the database takes time!) along with error handling.

Below is the sample code for User Schema

import mongoose from "mongoose";

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

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

export default User;

In the above code, we define the schema for a user where userSchema is a structure of the User collection in MongoDB. Inside the userSchema, we have three entries that store name, email, and password. We make sure the email should be unique, and all three fields are required. By adding timestamps: true, it will automatically add two fields (createdAt and updatedAt) to track when the user was created/updated.

const User = mongoose.model("User", userSchema); this line creates a Mongoose model named User to perform CRUD operations, and in the end, we export the User model so that it can be used in other parts of the code as well.

View

In the MVC pattern, the View represents the user interface (UI) and it is responsible for displaying the information to the user. It is the frontend part of the application that users interact with. The View takes the data provided by the Controller and renders it in a visually appealing and accessible way for the user. In a MERN stack project, the View is typically built using React, which dynamically updates the UI based on the data received from the Controller. It is important to note that the View does not handle any business logic or data manipulation; it simply presents the data in a structured manner.

Controller

The main logic is written this folder where you have to define diffence file for each for exmaple for user you have to define the user.controller.js where you have write logic releted to user(user login user singup user list etc. ) same for other thing like if your project is releted to ecommerce then you have to write logic for prouduct like that below it the sample code for user creation

import User from "../models/user.model.js";  

export const createUser = async (req, res) => {
  try {
    const { name, email, password } = req.body;

    // Check if user already exists
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      return res.status(400).json({ message: "User already exists" });
    }

    // Create a new user
    const newUser = new User({ name, email, password });
    await newUser.save();

    res.status(201).json({ message: "User created successfully", user: newUser });
  } catch (error) {
    res.status(500).json({ message: "Error creating user", error: error.message });
  }
};

In the above code, you can see that first we import the user model to gain access to all users stored in the database. Then, we start writing the logic for creating a user. First, we get the required fields from the frontend side (you can assume that on the frontend, we have a form with three fields, and the submit button is linked to an API, which will pass the data to this controller through the router). Then, from the email, we first check if an account with that email already exists. If yes, we send a message back to the frontend that the user already exists and suggest logging in instead of signing up. Otherwise, we create a new user with the given parameters, save it in the database, and send a message that the user has been created.

I hope you enjoyed this article! If you found any mistakes or have any suggestions, feel free to connect with me on LinkedIn or X. Your feedback is always welcome!

Did you find this article valuable?

Support Shivam's Blog❤️❤️ by becoming a sponsor. Any amount is appreciated!