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


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 pagePOST /register
β CallregisterUser()
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?
Method | Purpose |
GET | Used to render views (like form pages) |
POST | Used to submit form data to the server |
GET /register | Shows the form |
POST /register | Submits the form and calls the backend logic |
π§ Final Summary
Every request follows this chain:
Client β Router β Controller β Model β Response
Use
GET
for showing pages andPOST
for handling form submissionsStructure 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
Subscribe to my newsletter
Read articles from Nitin Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
