December 7, 2023

Structuring a node.js MVC application

MVC

MVC stands for (model view controller) in which model is something that holds the database part of the application, this part is well represented by mongoDB and it goes well with nodejs. View is something that holds the rendering (UI) part of the app, and lastly controller is the part which handles all the incoming requests to the app.

Purpose of MVC

The purpose of MVC is to help developers, focus at 1 aspect of software at a time, hence it divides the app in 3 parts and makes it easy for teams to focus on any aspect given to them.

MVC in action

The Model updates and communicates with view and renders itself on the App, The View sends requests to controllers from here there are 2 possibilities, 1. the controller sends some response back to view, 2. as the controller recieves the request it modifies it and appropriately updates the specific Model. Later the Model sends the updated changes to UI which gets rendered on the screen.

Structuring a NodeJs MVC app

Structuring requires 3 folders.

  • models
  • views
  • controllers
created folders, in which sub-folders can created stating different models,views,controllers.

Models

Models folder holds all the different models created in mongoDB with help of mongoose. For instance we can create model using mongoose.schema

import mongoose from "mongoose";
const UserSchema = new mongoose.Schema({
  username: {
    type: String,
    require: true,
  },
  password: {
    type: String,
    require: true,
  },
});
export const userModel = mongoose.model("User", UserSchema);

above code creates a model like structure which is used later in controllers.

Controllers

controllers holds the logic to modify incoming requests, these requests differ on basis of data that comes with it. The data is mostly found on req.body and this comes from UI which is present in Views folder

export const postRegisterController = async (req, res) => {
  const { username, password, confirmPassword } = req.body;
  console.log({ username, password, confirmPassword });
  if (password !== confirmPassword) {
    res.send("Passwords do not match");
  } else {
    const user = await userModel.findOne({ username: username });
    if (user) {
      res.send("user already registered");
    } else {
      const newUser = new userModel({ username, password });
      console.log("new user Registered", newUser);
    }
  }
};

Views

Views folders consists of different javascript frameworks such as React,Angular. Here in below example we have used ‘.ejs‘ which is a templating language, to know more about templating-language click here.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Login</title>
    <h2>Register User</h2>
    <br />
    <br />
    <form method="post" action="/register">
      <span>
        <label>Username</label>
        <input name="username" id="username" type="text" />
      </span>
      <span>
        <label>Password</label>
        <input name="password" id="password" type="password" />
        <label>Confirm Password</label>
        <input name="confirmPassword" id="confirmPassword" type="text" />
      </span>
      <button type="submit">Register</button>
    </form>
  </head>
</html>

Main App

we have a index.js file which connects connects controllers to models and registers views on controllers, so that everything works in the require manner.

import express from "express";
import {
  loginController,
  postLoginController,
} from "./controllers/login-controller.mjs";
import {
  postRegisterController,
  registerController,
} from "./controllers/register-controllers.mjs";
import mongoose from "mongoose";
import * as dotenv from "dotenv"; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
dotenv.config();
const port = process.env.PORT || 4123;
mongoose
  .connect("mongodb://localhost:27017/Cluster0?directConnection=true", {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => console.log("e don connect"))
  .catch((err) => console.log("could not connect", err));
const app = express();
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: false }));
app.get("/login", loginController);
app.get("/register", registerController);
app.post("/login", postLoginController);
app.post("/register", postRegisterController);
app.listen(port, () => {
  console.log("listening on ", port);
});

Conclusion

Congratulations! You have seen this through. I hope you enjoyed and learned a lot about how to structure and build your next application using the MVC architectural pattern.

Note that with this structure, you can extend it to whatever application you want to create, including a full application.