DEV Community

Yash Patil
Yash Patil

Posted on • Originally published at yashpatilofficial.hashnode.dev

Containerizing a MERN Stack Application!

In this blog, I’ll walk you through a simple yet powerful project that demonstrates the MERN stack in action. We will go through what MERN is but also how it works together to create a robust application. Plus, we’ll explore how Docker helps containerize and orchestrate everything seamlessly.

Here is the Github link for the project which consists of the source code and the Docker-Compose file used to containerize the application.

Image description

NOTE: This application is an open-source project to showcase the process of containerizing existing applications.

Before beginning to containerize the application lets first understand what is MERN framework is and its architecture and structure and how it works.

What is MERN?

MERN stands for MongoDB, Express.js, React, and Node.js—a popular stack for building full-stack JavaScript applications. Here’s how each component contributes:

  1. MongoDB: A NoSQL database that stores data in JSON-like documents, and acts as the database layer.

  2. Express.js: A lightweight web framework for Node.js, used to build business logic of the application and manage HTTP requests and define API endpoints.

  3. React: A frontend library for building dynamic and responsive user interfaces and client side logic. It fetches data from the backend via REST API or graphQL.

  4. Node.js: A JavaScript runtime environment that powers the backend, enabling JavaScript to run on the server. It acts a virtual server on top of our servers or host system on which the application might be hosted. Hence it hosts the application, allowing it to run and be accessed over the internet.

Together, MERN allows developers to write the entire application in JavaScript, streamlining the development process.

For this basic MERN stack application which allows us to Create, View, Edit and Delete employee records, we have a frontend and a backend folder.

The backend of this MERN stack application is designed to handle data operations for managing employee records. Here’s a breakdown of how the connection.js, records.js, and server.js files interact to make everything function seamlessly.

Image description

1. Database Connection: /db/connection.js

This file establishes a connection to the MongoDB database.

  • Client : In this case, the database server is named mongodb (as defined in the docker-compose.yml file), and it listens on port 27017. This the database connection string and we can also read this connection string using a environment variable or a config file.

Image description

  • Database Reference: The employees database is referenced using client.db("employees"), and this reference is exported as db. This enables other parts of the application, such as API routes, to interact with the database.

Image description

2. API Routes: records.js

The records.js file defines a set of RESTful API endpoints for CRUD operations (Create, Read, Update, Delete). These routes use the express.Router to group and manage endpoints under the /record path and defines RESTful routes to handle requests for employee data using the db object.

3. Server Initialization: server.js

The server.js file is the entry point for the backend application.

Image description

  • Express App: An Express application is created to handle HTTP requests and responses.

  • Middleware:

    • cors: Enables Cross-Origin Resource Sharing, allowing the frontend (on a different port) to communicate with the backend.
    • express.json: Parses incoming JSON payloads in request bodies.
  • Route Mounting: The records routes are mounted at the /record path. For example, a request to /record is handled by the logic defined in records.js.

  • Server Startup: The app listens on port 5050 (or another port if specified in the PORT environment variable). A message is logged to confirm the server is running.

So basically that sums up the backend logic of the application.

  • When a request is made from the browser using the REACTjs frontend application, the Nodejs listens for the incoming HTTP requests on a specified port.

  • Passes the request to Express.js which matches the request to the defined route.

  • It executes the corresponding logic, like querying(CRUD operations) the database or prepare a response, etc.

  • Then Node takes the response by Express.js and sends it back to the Browser where it is displayed to the user via the React.js frontend application.

And that’s the actual workflow of a MERN stack application.

Containerizing the Application

What is Docker Compose?

Docker Compose is a tool that simplifies the management of multi-container applications. Using a docker-compose.yml file, you can define and configure services, networks, and volumes needed for your application. With a single command, Docker Compose can build, start, and orchestrate all the containers defined in the file.

Now lets begin to containerize the application. The docker-compose.yml file orchestrates the three services (frontend, backend, and MongoDB) to work together seamlessly. I have created Dockerfiles for both the frontend and the backend which we will run as separate containers and create a MongoDB container using a image from the DockerHub.

services:
  frontend:
    build: ./mern/frontend
    ports:
      - "5173:5173"
    networks:
      - mern

  backend:
    build: ./mern/backend
    ports:
      - "5050:5050"
    networks:
      - mern
    depends_on:
      - mongodb

  mongodb:
    image: mongo
    ports:
      - "27017:27017"
    networks:
      - mern
    volumes:
      - mongo-data:/data/db

networks:
  mern:
    driver: bridge

volumes:
  mongo-data:
    driver: local
Enter fullscreen mode Exit fullscreen mode

Frontend Service:

- build: ./mern/frontend`: Specifies the Dockerfile for the frontend 
   application located in the `mern/frontend` directory.

- ports: "5173:5173"`: Maps port 5173 on the host to port 5173 in the container, making the React app accessible.

- networks: `mern`: Connects the service to the `mern` network for inter-service communication.
Enter fullscreen mode Exit fullscreen mode

Backend Service:

* `build: ./mern/backend`: Specifies the Dockerfile for the backend application located in the `mern/backend` directory.

* `ports: "5050:5050"`: Maps port 5050 on the host to port 5050 in the container for backend access.

* `depends_on: mongodb`: Ensures MongoDB starts before the backend.

* `networks: mern`: Connects the service to the `mern` network.
Enter fullscreen mode Exit fullscreen mode

MongoDB Service:

* `image: mongo`: Uses the official MongoDB image to run the database.

* `ports: "27017:27017"`: Maps port 27017 on the host to port 27017 in the container for database access.

* `volumes: mongo-data:/data/db`: Mounts a Docker volume named `mongo-data` to `/data/db` inside the container for persistent storage. The volume is mounted to `/data/db` inside the MongoDB container because `/data/db` is the default directory used by MongoDB to store its database files.

* `networks: mern`: Connects the service to the `mern` network.
Enter fullscreen mode Exit fullscreen mode

Networks:

* `mern`: A custom bridge network allowing the frontend, backend, and MongoDB services to communicate.
Enter fullscreen mode Exit fullscreen mode

Volumes:

* `mongo-data`: A named volume to persist MongoDB data between container restarts. This volume is created under `/var/lib/Docker/volumes`
Enter fullscreen mode Exit fullscreen mode

Now make sure you are in the directory where the docker-compose.yml is present and use to following command to spin up all the containers :

docker-compose up
Enter fullscreen mode Exit fullscreen mode

Note: For the first time it may take some time to build the images and run the containers.

Image description

Now access the frontend application at localhost:5173 where out React frontend application is running.

Image description

Image description

Now even if i stop and recreate all the containers, the records will still be there since we have used a host volume and mounted it to the mongoDB container. This allows the data persistence.

Stop all containers using :

docker-compose down
Enter fullscreen mode Exit fullscreen mode

Image description

And that’s how easy it is to work with docker compose in a multi container environment.

Summary

  1. Frontend: A React app running on port 5173, providing a user-friendly interface for managing employee records.

  2. Backend: A Node.js API on port 5050, processing business logic and interacting with MongoDB.

  3. Database: MongoDB, configured with a connection URL and persisting data via a Docker volume.

  4. Docker Compose: Orchestrates the entire stack, making it easy to spin up the application.

Going forward the entire workflow of a MERN stack application is pretty much similar with a few teaks here and there but the logic remains the same.

Always make sure the to go through the application codebase and understand how the application works before proceeding forward which will make it easy to use tools like Docker or Docker-Compose.

Feel free to checkout my blog : yashpatilofficial.hashnode.dev & dev.to/yash_patil16

And connect with me at LinkedIn : https://www.linkedin.com/in/yash-patil-24112a258/

Top comments (0)