Understanding Route & Request Flow in Node.js MVC | Beginner-Friendly Guide

Nitin KumarNitin Kumar
4 min read

Learn MVC - The easiest way


🧠 Introduction

When working on backend projects using Node.js + Express, most beginners struggle with this question:

❓ β€œHow does a route actually flow through the app? From client β†’ to controller β†’ to response?”

In this post, we’ll break down every step of a request β€” using a simple Candy Store app built in MVC architecture.

You’ll learn:

  • What happens when a route is called

  • The GET vs POST flow

  • The role of router, controller, and model

  • And how to make your project clean and scalable

With code examples included βœ…


πŸ“ Project Folder Structure (MVC Pattern)

/project-root
β”‚
β”œβ”€β”€ controllers/
β”‚   └── authController.js
β”‚
β”œβ”€β”€ models/
β”‚   └── user-model.js
β”‚
β”œβ”€β”€ routes/
β”‚   └── authRoutes.js
β”‚
β”œβ”€β”€ utils/
β”‚   └── token-generator.js
β”‚
β”œβ”€β”€ views/
β”‚   β”œβ”€β”€ home.ejs
β”‚   β”œβ”€β”€ register.ejs
β”‚   └── login.ejs
β”‚
β”œβ”€β”€ app.js or server.js

This structure helps us separate logic and makes the project easy to scale and debug.


πŸ”„ Full Route Flow Breakdown

Let’s walk through an actual example:

1️⃣ Client Sends Request

User opens the browser and visits:

GET /

This request first hits your main app.js.


2️⃣ app.js Handles Base Routes

const express = require("express");
const app = express();

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

app.use("/", authRoutes);

app.listen(5000, () => {
  console.log("Server is running on port 5000");
});

The app says:

β€œAll / related routes are handled by authRoutes.”

Now the request moves to authRoutes.js.


3️⃣ Routes File Handles Path Matching

const express = require("express");
const router = express.Router();
const { registerUser, loginUser, logoutUser } = require("../controllers/authController");

router.get("/", (req, res) => {
  res.render("home"); // home.ejs will be rendered
});

router.get("/register", (req, res) => {
  res.render("register");
});

router.post("/register", registerUser);

router.get("/login", (req, res) => {
  res.render("login");
});

router.post("/login", loginUser);

router.get("/logout", logoutUser);

module.exports = router;

πŸ”‘ This is where the real routing logic lives:

  • GET /register β†’ Render register page

  • POST /register β†’ Call registerUser() controller function


4️⃣ Controller Logic Handles Business Logic

// controllers/authController.js

const User = require("../models/user-model");
const bcrypt = require("bcrypt");
const generateToken = require("../utils/token-generator");

exports.registerUser = 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).send("User already exists");
    }

    // Hash password
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(password, salt);

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

    // Generate JWT token
    const token = generateToken(newUser._id);

    // Send token as a cookie
    res.cookie("jwt", token, { httpOnly: true });

    res.redirect("/");
  } catch (err) {
    res.status(500).send("Error registering user");
  }
};

This controller:

  • Handles form data

  • Checks for existing user

  • Hashes the password using bcrypt

  • Creates a user in DB

  • Generates a JWT token

  • Sets the token in a secure cookie


5️⃣ User Model Talks to MongoDB

// models/user-model.js

const mongoose = require("mongoose");

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

module.exports = mongoose.model("User", userSchema);

This file handles data schema β€” used in the controller to create or find users.


6️⃣ Token Generator Utility

// utils/token-generator.js

const jwt = require("jsonwebtoken");

const generateToken = (id) => {
  return jwt.sign({ id }, process.env.JWT_SECRET, {
    expiresIn: "1d",
  });
};

module.exports = generateToken;

βœ… JWT is used for authentication β€” it stores user identity securely in cookies so the user stays logged in.


πŸ”„ Visual Flow (Simple Diagram)

Browser
  ↓
GET /
  ↓
app.js
  ↓
authRoutes.js β†’ GET / β†’ render("home")
  ↓
Controller (if POST)
  ↓
Model β†’ MongoDB
  ↓
Response sent back to client

πŸ§ͺ GET vs POST β€” Why Both?

MethodPurpose
GETUsed to render views (like form pages)
POSTUsed to submit form data to the server
GET /registerShows the form
POST /registerSubmits the form and calls the backend logic

🧠 Final Summary

  • Every request follows this chain:

    Client β†’ Router β†’ Controller β†’ Model β†’ Response

  • Use GET for showing pages and POST for handling form submissions

  • Structure your project using MVC for cleaner code

  • Always hash passwords and use JWT for authentication


πŸš€ Bonus Tip

When you document your code like this:

  • βœ… You understand your code better

  • βœ… Others can read and follow it

  • βœ… Makes your portfolio stand out


0
Subscribe to my newsletter

Read articles from Nitin Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nitin Kumar
Nitin Kumar