DEV Community

Tharindu Jayawardhana
Tharindu Jayawardhana

Posted on

Building Robust and Scalable MERN Backends: A Practical Guide

Image description

In the world of making websites and apps work smoothly, how we organize things behind the scenes is super important. Think of it like having a well-organized kitchen — it makes cooking (or coding) much easier! Because when the kitchen grows and it starts cooking more things(more features) it is very important to have a scalable file and folder structure. Understanding the file and folder structure helps you to scale your app in a effective way. Enough talk, let’s dive in.

File And Folder Structure

Image description

Let’s understand one by one.


Purpose: The app.js file is often the entry point of your backend application. It's where you initialize your server, set up middleware, and define how routes are handled.
Example Usage: Inside app.js, you might set up Express (or another framework), configure middleware, and connect routes to their respective controllers.
Note that sometimes some developers implement above code in index.js file. But for the clarity I prefer making a separate app.js file.


Purpose: The index.js file is another entry point for your application. It's commonly used to start the server or bootstrap the application. It may import and execute code from other files, initializing the overall application structure.
Example Usage: In index.js, you might import and run app.js, initiating the server and starting your backend application.

config Folder:

Purpose: The config folder typically contains configuration files for your backend application. These files may include settings related to databases, environment variables, third-party services, and other global configurations.
Example Usage: You might have a database.js file inside config to specify your database connection details.

controller Folder:

Purpose: The controller folder is where you handle the application's business logic. Each file in this folder may represent a different aspect or feature of your application, managing the logic behind the routes.
Example Usage: A file named userController.js could handle user-related logic, such as user authentication, registration, and profile management.

helpers Folder:

Purpose: The helpers folder typically contains utility functions or helper modules that are used across different parts of your application. These functions can assist with common tasks, enhancing code reusability.
Example Usage: You might have a validationHelper.js file inside helpers that contains functions for validating user input.

middleware Folder:

Purpose: The middleware folder is where you store functions or modules that intercept and process requests before they reach the route handler. Middleware functions can be used for tasks like authentication, logging, or modifying request data.
Example Usage: A file named authMiddleware.js could handle user authentication before allowing access to certain routes.

models Folder:

Purpose: The models folder typically contains files representing data structures and interactions with your database. Each model file defines how data is structured and may include operations for retrieving, updating, and deleting data.
Example Usage: A file named User.js could define the structure of a user in your application and include functions for database operations related to users.

routes Folder:

Purpose: The routes folder is where you define the different routes or endpoints of your API. Each file in this folder corresponds to a specific route or a group of related routes.
Example Usage: A file named userRoutes.js could define routes related to user authentication, such as login, create user, update user and delete user endpoints.

Example Code Snippets

Image description
app.js file

Above is the app.js, here you can see there is server initialization and then some middleware setups. Also bottom you can see some different routes are handled.

Image description

Here is the index.js file which use to start the server and boottrap your application. You can see that it imports the server from app.js file hiding the code behind app.js file which helps to maintain the code clarity.

Image description

database.js file in config folder
Here is the database.js file that configure the database connectivity. Here I have used mongodb as the database. This file is in the config folder. This connectDB() function is imported to app.js file. When application starts the database connection will be automatically done.

Image description

userRoutes.js file in routes folder
In the app.js file there were some routes handled. Here is the full rout file. Here you can see various routes for various operations on user. As well as you can see in each route there are some specific functions. These routes will be called from the frontend to access the userinfo and do some operations on them.

Image description

userController.js file in the controller folder
Previously in routes, there was specific functions for each route. Like as an example

Enter fullscreen mode Exit fullscreen mode

In this route there is a function called, that is createUser. So this createUser function is actually implemented in the userController.js file. Similarly for other routes , the relevant functions are implemented on the controller file. That means this is where the logic happens.

Image description

authMiddleware.js file in middleware folder
For some API endpoints, to give the access to user we have to verify the user. For a situation like that we can use a middleware. Here checkAuth is a middleware function which checks whether the user is logged in or not before giving access to some API endpoints.

As an example we can take,

router.route('/updateuser/:id').put(checkAuth, updateUser);
Enter fullscreen mode Exit fullscreen mode

Here you can see before updateUser function it checks the checkAuth function. If the checkAuth is successful then user can access the updateUser endpoint otherwise he can’t.

Image description

authHelper.js file in helpers folder
This is an example for a helper class where we store reusable code snippets. This example shows some functions that can be reused in the code. This might be not necessary but for keeping code clear I follow thi approach.

Image description

User.js file in models folder
Inside user.js , we define the schema for a user using a technology like Mongoose, which is a MongoDB object modeling tool designed to work in an asynchronous environment like Node.js. The schema outlines the properties and data types associated with a user, such as username, email, password (hashed for security), and any additional information needed for your application.

Very high level overview of the way an endpoint works

Image description

  1. Request Arrival:
    A request is sent to a specific endpoint (e.g., /createuser).

  2. Middleware Interception (Optional):
    If applicable, middleware functions are executed to perform preliminary tasks like authentication or logging.

  3. Route Matching:
    The request is matched to the appropriate route defined in the routes folder.

  4. Controller Function Execution:
    The corresponding controller function, containing the business logic, is called.

  5. Model Interaction (Optional):
    If data needs to be retrieved or modified, controller functions interact with models in the models folder to access the database.

  6. Response Generation:
    The controller function prepares a response, often containing data or a success/error message.

  7. Response Sending:
    The response is sent back to the client.

Key Points for Maintainability and Scalability

  1. Clear Separation of Concerns: The structure promotes modularity, making code easier to understand, maintain, and test.
  2. Modular Middleware: Middleware enhances code organization and reusability, allowing for flexible request handling.
  3. Organized Models: Models encapsulate data interactions, ensuring consistency and simplifying database operations.
  4. Dedicated Controllers: Controllers handle business logic, keeping code focused and promoting reusability.
  5. Centralized Routes: Routes define API endpoints in a clear and manageable way.
  6. Helper Functions for Reusability: Helpers promote code efficiency and maintainability by encapsulating common tasks.

Additional Considerations:

  1. Error Handling: Implement robust error handling mechanisms to ensure graceful error responses and maintain application stability.
  2. Testing: Write comprehensive tests for controllers, models, and middleware to ensure code quality and prevent regressions.
  3. Documentation: Provide clear documentation for each component to guide collaboration and maintainability.
  4. Security: Implement security measures to protect against vulnerabilities and unauthorized access, such as input validation and authentication.
  5. Remember: Adapt this structure to your specific needs and preferences, and continuously evaluate and refine it as your application grows.

So I hope this helps you to get good understanding on backend structure for a simple scalable web app. If you want to explore more you can visit my github repo. Thank you for reading!.

Top comments (1)

pramithamj profile image
Pramitha Jayasooriya

This is one of the better Structures I've read on this topic, well done!