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.
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:
MongoDB: A NoSQL database that stores data in JSON-like documents, and acts as the database layer.
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.
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.
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.
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 thedocker-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.
-
Database Reference: The
employees
database is referenced usingclient.db("employees")
, and this reference is exported asdb
. This enables other parts of the application, such as API routes, to interact with the database.
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.
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 inrecords.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
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.
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.
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.
Networks:
* `mern`: A custom bridge network allowing the frontend, backend, and MongoDB services to communicate.
Volumes:
* `mongo-data`: A named volume to persist MongoDB data between container restarts. This volume is created under `/var/lib/Docker/volumes`
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
Note: For the first time it may take some time to build the images and run the containers.
Now access the frontend application at localhost:5173
where out React frontend application is running.
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
And that’s how easy it is to work with docker compose in a multi container environment.
Summary
Frontend: A React app running on port 5173, providing a user-friendly interface for managing employee records.
Backend: A Node.js API on port 5050, processing business logic and interacting with MongoDB.
Database: MongoDB, configured with a connection URL and persisting data via a Docker volume.
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)