
Full-Stack Engineer
May 22, 2026
β°6 min read
ποΈ138 views

Building scalable MERN apps starts with a solid foundation. Learn the best practices for folder structures, routing, controllers, and environment variables.
The MERN stack (MongoDB, Express.js, React, Node.js) is one of the most popular technology stacks for building full-stack web applications. It allows developers to write the entire applicationβfrom the database queries to the user interfaceβusing JavaScript.
However, as a MERN application grows, maintaining a clean and scalable codebase becomes challenging. Without a standard folder structure, code becomes tangled, making debugging a nightmare and collaborating with other developers difficult.
In this guide, we break down the best practices for structuring a production-ready MERN stack application, separating the backend (Node/Express) and frontend (React).
When starting a MERN project, the first decision is how to store the code.
/client and /server folders. This is the recommended approach for solo developers, student projects, and small teams, as it keeps everything in one place.For this guide, we assume a Monorepo structure:
my-mern-app/
βββ client/ # React Frontend
βββ server/ # Node/Express Backend
βββ .gitignore
βββ README.md
The backend of a MERN app handles API requests, database interactions, and authentication. We use a modified MVC (Model-View-Controller) architecture, replacing "View" with JSON responses.
A scalable Express backend should look like this:
server/
βββ src/
β βββ config/ # DB connection, third-party API configs
β βββ controllers/ # Request handlers and business logic
β βββ middlewares/ # Custom middlewares (auth, error handling)
β βββ models/ # Mongoose schemas
β βββ routes/ # Express route definitions
β βββ utils/ # Helper functions (hashing, formatting)
β βββ index.js # Entry point of the application
βββ .env # Environment variables
βββ package.json
βββ README.md
routes.js file is just a clean map of endpoints, while the heavy lifting happens in the controllers.Let's look at how these three folders interact to handle a user registration request.
1. The Model (models/User.js)
Defines the structure of the document in MongoDB using Mongoose.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
}, { timestamps: true });
module.exports = mongoose.model('User', userSchema);
2. The Route (routes/authRoutes.js)
Maps HTTP methods (GET, POST) and endpoints to specific controller functions.
const express = require('express');
const { registerUser } = require('../controllers/authController');
const router = express.Router();
router.post('/register', registerUser);
module.exports = router;
3. The Controller (controllers/authController.js)
Contains the business logic: validating inputs, hashing passwords, and saving to the database.
const User = require('../models/User');
const registerUser = async (req, res) => {
try {
const { name, email, password } = req.body;
// Logic to hash password and save user...
res.status(201).json({ message: "User registered successfully!" });
} catch (error) {
res.status(500).json({ error: "Server error" });
}
};
module.exports = { registerUser };
Modern React applications are usually built with Vite for speed. Structuring a React app is about grouping components logically.
client/
βββ src/
β βββ assets/ # Images, global CSS, fonts
β βββ components/ # Reusable UI components (Buttons, Modals)
β βββ context/ # React Context API files (Global state)
β βββ hooks/ # Custom React hooks (useAuth, useFetch)
β βββ pages/ # Page-level components (Home, Dashboard)
β βββ services/ # API call functions (axios instances)
β βββ utils/ # Helper functions (date formatting)
β βββ App.jsx # Main router setup
β βββ main.jsx # React DOM render
βββ .env
βββ package.json
βββ vite.config.js
Dashboard.jsx file is 500 lines long, break it down into smaller components (e.g., Sidebar.jsx, StatsCard.jsx).fetch() or axios calls directly inside your UI components. Move them to the services/ folder to keep your components clean and testable.Environment variables store sensitive information like database URIs, JWT secrets, and API keys.
.env: Ensure .env is listed in your .gitignore file..env.example: Create a dummy file with empty variable names so other developers know what environment variables are required to run the project.# .env.example
PORT=5000
MONGO_URI=your_mongo_db_connection_string
JWT_SECRET=your_secret_key
For small to medium MERN apps, the native React Context API (combined with local state) is usually sufficient. For large-scale enterprise applications with complex, frequently updating states, Redux Toolkit is the better choice.
Use a middleware like Multer in your Express backend to handle multipart/form-data. Store the actual files in cloud storage like AWS S3 or Cloudinary, and save the resulting image URL in MongoDB.
Next.js is a meta-framework built on top of React that provides Server-Side Rendering (SSR) and SEO benefits. If your MERN app requires good SEO (like a blog or e-commerce site), consider replacing standard React with Next.js for the frontend.
A well-structured MERN stack application drastically reduces development time and prevents technical debt. By keeping your routes, models, and controllers cleanly separated on the backend, and abstracting API calls and reusable components on the frontend, you create a codebase that is scalable and professional. Start applying these patterns to your next project!
Suggested Images:
MERN stack technologies connected in an isometric server diagram, vibrant green and dark blue aesthetic).Alt Texts:
Internal Linking Suggestions:

Full-Stack Engineer
Shubham Shukla is a full-stack engineer and tech educator, specializing in building robust, production-ready MERN and Next.js applications.
Loading comments...