Node.js MVC Pattern Practice Questions with Solutions

Introduction

The MVC pattern helps organize a Node.js application into separate parts: Model, View, and Controller. Instead of keeping database logic, application logic, and page responses inside one large file, MVC separates these responsibilities. In this chapter, you will practice MVC step by step, starting with a basic structure and gradually building controllers, models, routes, views, middleware, and a simple CRUD-style application using Node.js and Express.js. Node.js MVC Pattern practice questions with solutions help to build concepts.

Question 1: What is the basic MVC folder structure in Node.js?

Problem

Create a simple Node.js project using the MVC pattern.

Solution

Create a project:

mkdir mvc-app
cd mvc-app
npm init -y
npm install express

Create this folder structure:

mvc-app/
│
├── controllers/
│   └── userController.js
│
├── models/
│   └── userModel.js
│
├── routes/
│   └── userRoutes.js
│
├── views/
│   └── userView.js
│
├── app.js
└── package.json

Step-by-Step Explanation

MVC stands for:

M = Model
V = View
C = Controller

Model

The Model manages data and data-related operations.

Example:

models/
└── userModel.js

View

The View is responsible for what the user sees.

Example:

views/
└── userView.js

Controller

The Controller contains application logic and connects requests with models and views.

Example:

controllers/
└── userController.js

Routes

Routes decide which controller should handle a request.

routes/
└── userRoutes.js

Basic MVC Flow

Client
  ↓
Route
  ↓
Controller
  ↓
Model
  ↓
Controller
  ↓
View / Response
  ↓
Client

Important Point

MVC is an architectural pattern. It is a way of organizing application responsibilities rather than a special Node.js feature.


Question 2: How do you create a Model in an MVC application?

Problem

Create a simple user Model that contains user data and a function for retrieving users.

Solution

Create:

models/userModel.js

Add:

const users = [
    {
        id: 1,
        name: "Rahul"
    },
    {
        id: 2,
        name: "Priya"
    }
];

function getUsers() {

    return users;

}

module.exports = {
    getUsers
};

Step-by-Step Explanation

First, we create sample data:

const users = [
    {
        id: 1,
        name: "Rahul"
    },
    {
        id: 2,
        name: "Priya"
    }
];

Then create a function:

function getUsers() {
    return users;
}

Finally export it:

module.exports = {
    getUsers
};

Now another file can use the Model.

Question 3: How do you create a Controller in Node.js MVC?

Problem

Create a Controller that gets users from the Model and sends them as JSON.

Solution

Create:

controllers/userController.js

Add:

const userModel =
    require("../models/userModel");

function getUsers(
    req,
    res
) {

    const users =
        userModel.getUsers();

    res.json({
        success: true,
        users: users
    });

}

module.exports = {
    getUsers
};

Step-by-Step Explanation

Import the Model:

const userModel =
    require("../models/userModel");

Call the Model:

const users =
    userModel.getUsers();

Send the response:

res.json({
    success: true,
    users: users
});

Why use a Controller?

Without MVC, you might put everything inside app.js.

With MVC:

Route
  ↓
Controller
  ↓
Model

Each part has a clear responsibility.


Question 4: How do you create MVC routes?

Problem

Create a route that sends /users requests to the user Controller.

Solution

Create:

routes/userRoutes.js

Add:

const express = require("express");

const router =
    express.Router();

const userController =
    require("../controllers/userController");

router.get(
    "/users",
    userController.getUsers
);

module.exports = router;

Now create app.js:

const express = require("express");

const userRoutes =
    require("./routes/userRoutes");

const app = express();

app.use(
    userRoutes
);

app.listen(3000, () => {

    console.log(
        "Server running on port 3000"
    );

});

Test

Open:

http://localhost:3000/users

Response

{
    "success": true,
    "users": [
        {
            "id": 1,
            "name": "Rahul"
        },
        {
            "id": 2,
            "name": "Priya"
        }
    ]
}

Request Flow

GET /users
     ↓
userRoutes.js
     ↓
userController.getUsers
     ↓
userModel.getUsers()
     ↓
JSON Response

Important Point

Routes should generally focus on mapping URLs and HTTP methods to controller functions rather than containing large amounts of business logic.


Question 5: How do you create a View in a Node.js MVC application?

Problem

Create a simple View that displays a list of users.

Solution

For a simple beginner example, create:

views/userView.js

Add:

function userListView(users) {

    let html =
        "<h1>User List</h1>";

    html += "<ul>";

    users.forEach(user => {

        html += `
            <li>
                ${user.name}
            </li>
        `;

    });

    html += "</ul>";

    return html;

}

module.exports = {
    userListView
};

Update the Controller:

const userModel =
    require("../models/userModel");

const userView =
    require("../views/userView");

function getUsers(
    req,
    res
) {

    const users =
        userModel.getUsers();

    const html =
        userView.userListView(users);

    res.send(html);

}

module.exports = {
    getUsers
};

Test

Open:

http://localhost:3000/users

You will see:

User List

• Rahul
• Priya

Step-by-Step Explanation

The Model provides data:

const users =
    userModel.getUsers();

The View converts the data into HTML:

const html =
    userView.userListView(users);

The Controller sends the HTML:

res.send(html);

MVC Flow

Model
 ↓
Data
 ↓
Controller
 ↓
View
 ↓
HTML Response

Important Point

In production applications, template engines such as EJS, Pug, or Handlebars are often used instead of manually creating HTML strings.


Question 6: How do you create an MVC application for getting a single user?

Problem

Create an API:

GET /users/:id

that returns one user.

Solution

Step 1: Update the Model

models/userModel.js

const users = [
    {
        id: 1,
        name: "Rahul"
    },
    {
        id: 2,
        name: "Priya"
    },
    {
        id: 3,
        name: "Amit"
    }
];

function getUsers() {

    return users;

}

function getUserById(id) {

    return users.find(
        user =>
            user.id === id
    );

}

module.exports = {
    getUsers,
    getUserById
};

Step 2: Create Controller

controllers/userController.js

const userModel =
    require("../models/userModel");

function getUserById(
    req,
    res
) {

    const id =
        Number(req.params.id);

    const user =
        userModel.getUserById(id);

    if (!user) {

        return res.status(404).json({

            success: false,

            message:
                "User not found."

        });

    }

    res.json({

        success: true,

        user: user

    });

}

module.exports = {
    getUserById
};

Step 3: Create Route

routes/userRoutes.js

const express = require("express");

const router =
    express.Router();

const userController =
    require("../controllers/userController");

router.get(
    "/users/:id",
    userController.getUserById
);

module.exports = router;

Step 4: Test

Open:

http://localhost:3000/users/2

Response

{
    "success": true,
    "user": {
        "id": 2,
        "name": "Priya"
    }
}

If User Does Not Exist

Open:

http://localhost:3000/users/99

Response:

{
    "success": false,
    "message": "User not found."
}

Important Point

req.params.id is a string. In this example, Number() converts it to a number before the Model searches the array.


Question 7: How do you add a new user using the MVC pattern?

Problem

Create a POST API that adds a new user using:

POST /users

Solution

Model

Update models/userModel.js:

const users = [
    {
        id: 1,
        name: "Rahul"
    },
    {
        id: 2,
        name: "Priya"
    }
];

function getUsers() {

    return users;

}

function addUser(name) {

    const newUser = {

        id:
            users.length + 1,

        name:
            name

    };

    users.push(
        newUser
    );

    return newUser;

}

module.exports = {
    getUsers,
    addUser
};

Controller

Update controllers/userController.js:

const userModel =
    require("../models/userModel");

function createUser(
    req,
    res
) {

    const {
        name
    } = req.body;

    if (
        !name ||
        name.trim() === ""
    ) {

        return res.status(400).json({

            success: false,

            message:
                "Name is required."

        });

    }

    const user =
        userModel.addUser(
            name.trim()
        );

    res.status(201).json({

        success: true,

        message:
            "User created successfully.",

        user:
            user

    });

}

module.exports = {
    createUser
};

Route

router.post(
    "/users",
    userController.createUser
);

app.js

Make sure JSON parsing is enabled:

const express = require("express");

const userRoutes =
    require("./routes/userRoutes");

const app = express();

app.use(
    express.json()
);

app.use(
    userRoutes
);

app.listen(3000, () => {

    console.log(
        "Server running on port 3000"
    );

});

Test Request

POST http://localhost:3000/users

JSON:

{
    "name": "Amit"
}

Response

{
    "success": true,
    "message": "User created successfully.",
    "user": {
        "id": 3,
        "name": "Amit"
    }
}

MVC Flow

POST /users
     ↓
Route
     ↓
Controller
     ↓
Model
     ↓
Create User
     ↓
Controller
     ↓
JSON Response

Important Point

The Controller validates the incoming request, while the Model handles the data operation.


Question 8: How do you separate validation middleware from an MVC Controller?

Problem

Create middleware that validates a user’s name before the request reaches the Controller.

Solution

Create:

middleware/
└── validateUser.js

Add:

function validateUser(
    req,
    res,
    next
) {

    const {
        name
    } = req.body;

    if (
        typeof name !== "string" ||
        name.trim() === ""
    ) {

        return res.status(400).json({

            success: false,

            message:
                "Name is required."

        });

    }

    next();

}

module.exports = {
    validateUser
};

Update the route:

const express = require("express");

const router =
    express.Router();

const userController =
    require("../controllers/userController");

const {
    validateUser
} = require("../middleware/validateUser");

router.post(
    "/users",
    validateUser,
    userController.createUser
);

module.exports = router;

Step-by-Step Explanation

The request first reaches:

POST /users

Then:

validateUser

checks the data.

If invalid:

400 Bad Request

If valid:

next();

passes control to:

userController.createUser

Complete Flow

Client
  ↓
Route
  ↓
Validation Middleware
  ↓
Valid?
 ↙       ↘
No       Yes
↓         ↓
400      Controller
           ↓
         Model
           ↓
        Response

Important Point

Separating validation into middleware can keep Controllers smaller and easier to maintain.


Question 9: How do you connect MongoDB with an MVC Node.js application?

Problem

Create a basic MVC structure where the Model communicates with MongoDB.

Solution

Install Mongoose:

npm install mongoose

Create:

models/userModel.js

Add:

const mongoose =
    require("mongoose");

const userSchema =
    new mongoose.Schema({

        name: {
            type: String,
            required: true
        },

        email: {
            type: String,
            required: true,
            unique: true
        }

    });

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

async function getUsers() {

    return await User.find();

}

async function createUser(
    data
) {

    return await User.create(
        data
    );

}

module.exports = {
    getUsers,
    createUser
};

Database Connection

Create:

config/database.js

Add:

const mongoose =
    require("mongoose");

async function connectDatabase() {

    await mongoose.connect(
        process.env.MONGO_URI
    );

    console.log(
        "MongoDB connected."
    );

}

module.exports =
    connectDatabase;

Controller

const userModel =
    require("../models/userModel");

async function getUsers(
    req,
    res,
    next
) {

    try {

        const users =
            await userModel.getUsers();

        res.json({

            success: true,

            users:
                users

        });

    } catch (error) {

        next(error);

    }

}

async function createUser(
    req,
    res,
    next
) {

    try {

        const user =
            await userModel.createUser(
                req.body
            );

        res.status(201).json({

            success: true,

            user:
                user

        });

    } catch (error) {

        next(error);

    }

}

module.exports = {
    getUsers,
    createUser
};

app.js

const express =
    require("express");

const connectDatabase =
    require("./config/database");

const userRoutes =
    require("./routes/userRoutes");

const app = express();

app.use(
    express.json()
);

connectDatabase()
    .then(() => {

        app.listen(
            3000,
            () => {

                console.log(
                    "Server running on port 3000"
                );

            }
        );

    })
    .catch(error => {

        console.error(
            "Database connection failed:",
            error
        );

    });

Step-by-Step Explanation

The Model contains the database logic:

User.find()

and:

User.create()

The Controller calls the Model:

await userModel.getUsers();

The Route connects the URL to the Controller.

MVC Architecture

Routes
   ↓
Controllers
   ↓
Models
   ↓
MongoDB

Then the result travels back:

MongoDB
   ↓
Model
   ↓
Controller
   ↓
Response

Important Point

Keeping database operations inside Models prevents database-specific logic from spreading throughout your route files.


Question 10: How do you build a complete MVC CRUD API?

Problem

Build a beginner-friendly CRUD API using the MVC pattern.

The API should support:

GET     /users
GET     /users/:id
POST    /users
PUT     /users/:id
DELETE  /users/:id

Solution

For this learning example, we will use an array instead of a database.

Project Structure

mvc-app/
│
├── controllers/
│   └── userController.js
│
├── models/
│   └── userModel.js
│
├── routes/
│   └── userRoutes.js
│
├── app.js
└── package.json

Step 1: Model

models/userModel.js

let users = [

    {
        id: 1,
        name: "Rahul",
        email: "rahul@example.com"
    },

    {
        id: 2,
        name: "Priya",
        email: "priya@example.com"
    }

];


function getUsers() {

    return users;

}


function getUserById(
    id
) {

    return users.find(
        user =>
            user.id === id
    );

}


function createUser(
    data
) {

    const newUser = {

        id:
            users.length > 0
                ? users[users.length - 1].id + 1
                : 1,

        name:
            data.name,

        email:
            data.email

    };

    users.push(
        newUser
    );

    return newUser;

}


function updateUser(
    id,
    data
) {

    const user =
        getUserById(id);

    if (!user) {

        return null;

    }

    if (
        data.name !== undefined
    ) {

        user.name =
            data.name;

    }

    if (
        data.email !== undefined
    ) {

        user.email =
            data.email;

    }

    return user;

}


function deleteUser(
    id
) {

    const userIndex =
        users.findIndex(
            user =>
                user.id === id
        );

    if (
        userIndex === -1
    ) {

        return null;

    }

    const deletedUser =
        users.splice(
            userIndex,
            1
        );

    return deletedUser[0];

}


module.exports = {

    getUsers,
    getUserById,
    createUser,
    updateUser,
    deleteUser

};

Step 2: Controller

controllers/userController.js

const userModel =
    require("../models/userModel");


function getUsers(
    req,
    res
) {

    const users =
        userModel.getUsers();

    res.json({

        success: true,

        users:
            users

    });

}


function getUserById(
    req,
    res
) {

    const id =
        Number(req.params.id);

    const user =
        userModel.getUserById(id);

    if (!user) {

        return res.status(404).json({

            success: false,

            message:
                "User not found."

        });

    }

    res.json({

        success: true,

        user:
            user

    });

}


function createUser(
    req,
    res
) {

    const {
        name,
        email
    } = req.body;

    if (
        !name ||
        !email
    ) {

        return res.status(400).json({

            success: false,

            message:
                "Name and email are required."

        });

    }

    const user =
        userModel.createUser({

            name:
                name.trim(),

            email:
                email.trim()

        });

    res.status(201).json({

        success: true,

        message:
            "User created successfully.",

        user:
            user

    });

}


function updateUser(
    req,
    res
) {

    const id =
        Number(req.params.id);

    const user =
        userModel.updateUser(
            id,
            req.body
        );

    if (!user) {

        return res.status(404).json({

            success: false,

            message:
                "User not found."

        });

    }

    res.json({

        success: true,

        message:
            "User updated successfully.",

        user:
            user

    });

}


function deleteUser(
    req,
    res
) {

    const id =
        Number(req.params.id);

    const user =
        userModel.deleteUser(id);

    if (!user) {

        return res.status(404).json({

            success: false,

            message:
                "User not found."

        });

    }

    res.json({

        success: true,

        message:
            "User deleted successfully.",

        user:
            user

    });

}


module.exports = {

    getUsers,
    getUserById,
    createUser,
    updateUser,
    deleteUser

};

Step 3: Routes

routes/userRoutes.js

const express =
    require("express");

const router =
    express.Router();

const userController =
    require("../controllers/userController");


router.get(
    "/users",
    userController.getUsers
);


router.get(
    "/users/:id",
    userController.getUserById
);


router.post(
    "/users",
    userController.createUser
);


router.put(
    "/users/:id",
    userController.updateUser
);


router.delete(
    "/users/:id",
    userController.deleteUser
);


module.exports = router;

Step 4: App

app.js

const express =
    require("express");

const userRoutes =
    require("./routes/userRoutes");

const app = express();

app.use(
    express.json()
);

app.use(
    "/api",
    userRoutes
);


app.listen(
    3000,
    () => {

        console.log(
            "Server running on port 3000"
        );

    }
);

Step 5: Test GET All Users

Request:

GET http://localhost:3000/api/users

Response:

{
    "success": true,
    "users": [
        {
            "id": 1,
            "name": "Rahul",
            "email": "rahul@example.com"
        },
        {
            "id": 2,
            "name": "Priya",
            "email": "priya@example.com"
        }
    ]
}

Step 6: Test GET One User

Request:

GET http://localhost:3000/api/users/1

Step 7: Test POST

Request:

POST http://localhost:3000/api/users

JSON:

{
    "name": "Amit",
    "email": "amit@example.com"
}

Step 8: Test PUT

Request:

PUT http://localhost:3000/api/users/1

JSON:

{
    "name": "Rahul Kumar"
}

Step 9: Test DELETE

Request:

DELETE http://localhost:3000/api/users/2

Complete MVC Flow

                CLIENT
                  |
                  ↓
               ROUTES
                  |
                  ↓
             CONTROLLER
                  |
                  ↓
                MODEL
                  |
                  ↓
          DATA / DATABASE
                  |
                  ↓
                MODEL
                  |
                  ↓
             CONTROLLER
                  |
                  ↓
              RESPONSE
                  |
                  ↓
                CLIENT

Important Point

This example uses an array, so data will disappear when the Node.js process restarts. In a real application, the Model would normally communicate with a persistent database.

Key Takeaways

1. MVC means Model, View, and Controller

MVC separates different responsibilities of an application.

2. Model handles data

The Model is responsible for data-related operations such as:

Create
Read
Update
Delete

3. Controller handles application logic

Controllers receive requests, call Models, process results, and prepare responses.

4. View handles presentation

The View is responsible for presenting information to the user.

5. Routes connect URLs to Controllers

For example:

router.get(
    "/users",
    userController.getUsers
);

6. MVC makes applications easier to organize

Instead of putting everything into app.js, responsibilities are divided into separate files.

7. Models can communicate with databases

For example, a Model can use Mongoose to communicate with MongoDB.

8. Middleware can be separated from Controllers

Validation, authentication, logging, and other reusable tasks can be placed in middleware.

9. Controllers should not become giant files

If a Controller contains too much logic, some responsibilities can be moved into services or other application layers.

10. MVC is an architectural pattern

Node.js does not force you to use MVC. You choose it as an application organization pattern.

11. req.params contains route parameters

For:

/users/10

you can access:

req.params.id

12. req.body contains request body data

When JSON middleware is enabled:

app.use(express.json());

you can access:

req.body

13. CRUD works naturally with MVC

A typical MVC API can separate CRUD operations between:

Routes
Controllers
Models

14. MVC can work with REST APIs

MVC is commonly used to organize Express.js REST API projects.

15. MVC is useful for larger applications

As an application grows, separating responsibilities can make the code easier to understand and maintain.

16. Use proper database storage in production

An in-memory JavaScript array is useful for learning but should not be used as the main data store for a production application.

17. Error handling should be separated

Large applications can use centralized Express error-handling middleware.

18. Authentication can fit into MVC

Authentication middleware can protect routes before requests reach Controllers.

19. Validation should happen before database operations

Incoming data should be validated before the Model stores or processes it.

20. MVC is not the only architecture

Larger Node.js applications may also use patterns such as service layers, repositories, or clean architecture alongside or instead of traditional MVC.

FAQs

1. What is MVC in Node.js?

MVC stands for Model, View, and Controller. It is an architectural pattern used to organize a Node.js application into separate responsibilities.

The Model handles data, the View handles presentation, and the Controller handles application logic.

2. What is the Model in Node.js MVC?

The Model handles data-related operations.

For example, a user Model may contain functions such as:

getUsers();
getUserById();
createUser();
updateUser();
deleteUser();

In a real application, the Model may communicate with MongoDB, PostgreSQL, MySQL, or another database.

3. What is a Controller in Node.js MVC?

A Controller receives the request, performs application-level logic, communicates with the Model, and sends the response.

For example:

async function getUsers(
    req,
    res
) {

    const users =
        await userModel.getUsers();

    res.json({
        users
    });

}

4. What is a View in Node.js MVC?

A View is responsible for presenting data to the user.

For server-rendered applications, the View may be an HTML template created using a template engine such as EJS, Pug, or Handlebars.

For a REST API, there may be no traditional server-side View because the API commonly returns JSON.

5. Why should I use MVC in Node.js?

MVC can make a project easier to organize as it grows.

Instead of placing routes, database queries, validation, and responses into one large file, responsibilities can be separated.

6. Is MVC required in Express.js?

No. Express.js does not require MVC.

You can create a small Express application in one file. MVC becomes useful when you want clearer separation of responsibilities and your project becomes larger.

7. What is the difference between MVC and REST API?

MVC is an application architecture pattern.

REST is a style of designing network APIs around resources, HTTP methods, and representations.

They can be used together:

REST API
   ↓
MVC Architecture
   ↓
Routes
Controllers
Models

Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.

Scroll to Top