DEV Community

Cover image for Middleware, Routes, and Controllers in MERN Stack-'How They Work Together'
Reshma Shaik
Reshma Shaik

Posted on

Middleware, Routes, and Controllers in MERN Stack-'How They Work Together'

MERN is a powerful full-stack framework that brings together four essential technologies for seamless web development.While React handles the frontend but, the real backbone of the backend is the connection between MongoDB, Express, and Node.

This blog unpacks how these components interact and their unique roles.

Consider a MERN stack project,which is structured as follows:

project-root/
├── client/        # Frontend (React)
└── server/        # Backend (Node.js + Express + MongoDB)
    ├── middleware/  # Handles authentication, logging, etc.
    ├── controllers/ # Processes business logic
    ├── routes/      # Defines API endpoints
    ├── models/      # Defines database schema
    ├── server.js    # Entry point of the backend
Enter fullscreen mode Exit fullscreen mode

How These Components Are Linked?

  1. Middleware(a function that has control over how the response is handled based on incoming requests.) is defined in server.js after initializing express framework.
  2. It then maps to routes( act as the URLs or links that process requests and direct them to the right place.), which define the API paths.
  3. Routes connect to controllers(manages the requests by determining which function should process a specific request.), which handle the logic of the request.
  4. Controllers interact with models(A user defined schema to based on client requirements), where the database schema is defined to store and manage data from the frontend.

Example

server.js

## Initializing express framework
const express = require('express');
const app = express(); // Initializes Express framework

##Middleware 
app.use((req, res, next) => {
  console.log(`Request received: ${req.method} ${req.url}`);
  next(); // Passes control to the next middleware or route handler
});
const users = require("./routes/users");
app.use("/api/users", users);
Enter fullscreen mode Exit fullscreen mode

Routing(routes/users.js)


const router = express.Router();
const userControllers = require("../controllers/userControllers");

router.post("/register", userControllers.register);
Enter fullscreen mode Exit fullscreen mode

Controllers(controllers/userControllers.js)

const User = require("../models/User");
const register = async (req, res) => {
  try {
    const { username, email, password } = req.body;//destructured based on schema refernce
    console.log(req.body);
    if (existingUser) {
      throw new Error("Email and username must be unique");
    }
    const user = await User.create({
      username,
      email,
      password,
    });
    return res.json(user);
  } catch (err) {
    return res.status(400).json({ error: err.message });
  }
};
module.exports = register; //which is fetched by routes as we imported there
Enter fullscreen mode Exit fullscreen mode

model(models/User.js)

const mongoose = require("mongoose");
const { isEmail, contains } = require("validator");
const filter = require("../util/filter");
const UserSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
      unique: true,
      minlength: [6, "Must be at least 6 characters long"],
      maxlength: [30, "Must be no more than 30 characters long"],
      validate: {
        validator: (val) => !contains(val, " "),
        message: "Must contain no spaces",
      },
    },
    email: {
      type: String,
      required: true,
      unique: true,
      validate: [isEmail, "Must be valid email address"],
    },
    password: {
      type: String,
      required: true,
      minLength: [8, "Must be at least 8 characters long"],
    }
  },
  { timestamps: true }
);
  next();
});//u can mention all constraints u want to add in a particular field based on the react registration form
module.exports = mongoose.model("user", UserSchema);//this helps to fetch this schema anywhere,when ever the import of the user is done like in controller section
Enter fullscreen mode Exit fullscreen mode

To gain a clear understanding, let's backtrack our approach:

The model's export is imported into the controller, the controller's export followed in router, maintaining a structured hierarchy.

Conclusion

By understanding and implementing these components, you're well-equipped to create efficient and organized backend systems.
If you found this guide helpful drop a like.Feel free to reach out with any questions or topics you'd like to discuss.
For any further queries reach out to me at -
1.Linkedin
2.Github
Thank You :)

Top comments (0)