A Dockerized Full-Stack To-Do List Application
Introduction :
This document outlines the design, architecture, and implementation of a full-stack, containerized To-Do List web application.
The app consists of a React frontend, a Node.js backend API, and a PostgreSQL database.
The main goal of this assignment is to demonstrate how Docker can create isolated, reproducible, and scalable environments.
Each component runs in its own container:
-
Frontend → custom image todo-app-fr
-
Backend → custom image todo-app-b
-
Database → postgres:1 official image
All three are managed using Docker Compose, which handles networking and orchestration.
Objectives of Part 1 :
-
Create a Dockerfile in the root project directory (todo-app/) for the React frontend.
-
Build a custom Docker image named todo-app-fr.
-
Run the frontend-1 container as a standalone service.
Objectives of Part 2 :
-
Create a Dockerfile in the backend/ directory for the Node.js API.
-
Build a custom Docker image named todo-app-b.
-
Run the backend-1 container as a standalone service.
-
Verify API endpoints using Postman.
Objectives of Part 3 :
-
Create a docker-compose.yml file defining frontend, backend, and database services.
-
Establish a Docker network for communication.
-
Launch the complete app using docker-compose up.
Containers Used :
-
node (e.g., node:18-alpine)
Purpose: Base image used for both frontend and backend.
Download: https://hub.docker.com/_/node
-
postgres:1
Purpose: Official image used for the database.
Download: https://hub.docker.com/_/postgres
Other Software Used :
-
Docker Desktop – to build, run, and manage containers
-
Visual Studio Code – for coding
-
Chrome / Safari – for testing frontend
-
Postman – to test backend APIs
Overall Architecture of the Project :
Architecture Summary:
User (Browser) → accesses the React frontend at http://localhost:3000
Frontend Container (frontend-1)
-
Built from the todo-app-fr custom image.
-
Handles all UI interactions and sends API requests to the backend.
-
Communicates with backend at port 3001 over Docker network.
Backend Container (backend-1)
-
Built from the todo-app-b custom image.
-
Implements RESTful APIs using Node.js and Express.
-
Processes CRUD requests and interacts with the PostgreSQL database.
-
Uses environment variables for database connection:
postgres://user:password@db:5432/todoapp
Database Container (db-1)
-
Runs on the official postgres:14-alpine image.
-
Stores task data (id, title, status, timestamps).
-
Data persisted in Docker named volume postgres_data.
Networking:
All containers (frontend, backend, db) are connected through a Docker Compose bridge network, allowing name-based communication (e.g., backend connects to db instead of IP address).
Healthchecks:
-
Frontend: Accessible via HTTP probe at http://localhost:3000/.
-
Backend: Checked via API endpoint http://localhost:3001/health (if implemented).
-
Database: Health verified using pg_isready command inside container.
Docker Hub:
Final custom images todo-app-fr and todo-app-b can be published to Docker Hub for reuse and deployment across systems.
Description about the architecture :
The To-Do List Application follows a multi-layer, 3-tier containerized architecture designed for modularity, scalability, and persistence. The system integrates a React-based web application (frontend) with a Node.js backend API and a PostgreSQL relational database, all orchestrated using Docker and Docker Compose for a fully automated deployment environment.
At the heart of the architecture is a React frontend container (service name: frontend), which serves as the primary interface for users. The container is built from a custom Dockerfile and exposes port 3000, allowing users to access the to-do list interface through any web browser. Users interact with the UI to view their task list, add new tasks, mark tasks as complete, or delete existing tasks. This container only handles presentation logic.
When a user performs an action (like adding a task), the React application sends an API request to the Node.js/Express backend container (service name: backend). This container, built from its own Dockerfile and exposing port 3001, is responsible for all business logic. It receives the JSON payload, validates the input, and executes the corresponding database operation (e.g., INSERT, SELECT, UPDATE, DELETE). It then formats a JSON response and sends it back to the frontend, which updates the UI.
Supporting the application is a PostgreSQL database container (service name: db), which functions as the persistent data storage layer. This container runs the official postgres image and maintains a structured table named todos, where all task details (e.t., id, text, completed status) are securely stored. The system uses a Docker volume (postgres_data) to persist database contents even when containers are stopped or rebuilt.
The frontend, backend, and database containers are connected through an internal Docker network (todo-net), which automatically handles service name resolution. This allows the frontend to send requests to the backend service and the backend to connect to the db service by their hostnames, eliminating manual IP configuration.
To ensure system reliability and smooth startup, the architecture employs Docker Compose for service orchestration. Docker Compose uses the depends_on condition to manage startup sequencing — ensuring the backend waits for the database to be ready and the frontend waits for the backend to start before they are launched. Once all containers are running, the system becomes operational without any manual intervention.
Security and maintainability are achieved by separating configuration details into environment variables in the docker-compose.yml file, keeping sensitive data such as database credentials (e.g., POSTGRES_USER, POSTGRES_PASSWORD) outside the application source code. Unnecessary build files (like node_modules) and cache directories are excluded from the images using a .dockerignore file, resulting in leaner and faster image builds.
The React (frontend) and Node.js (backend) containers are designed to remain stateless, meaning no critical data is stored inside them, while the PostgreSQL (db) container manages all persistent state through the attached volume. This separation ensures the system is both portable and fault-tolerant.
For this project, the custom images (todo-app-fr and todo-app-b) are built locally using the docker-compose up --build command. For wider deployment, these images could be published to a container registry like Docker Hub. This would enable anyone to reproduce the environment by pulling the images and running the same docker-compose.yml file, ensuring the system remains consistent across all environments.
The overall system architecture, as depicted in the diagram, is organized into distinct layers:
User Interaction Layer: Handles user input and UI rendering through the browser (React).
Frontend / Application Layer: The React container (
frontend) that serves the static UI.Backend / Business Logic Layer: The Node.js container (
backend) that processes API requests and manages logic.Data Layer: The PostgreSQL container (
db) that manages data storage and persistence.Infrastructure Layer: Defines the network, service dependencies, and volumes using Docker Compose.
This layered 3-tier design provides a clear separation of responsibilities, ensuring the To-Do List application is modular, reproducible, and highly portable. It allows smooth transitions between development, testing, and deployment, while maintaining system reliability and data consistency across container restarts or environment changes.
Procedure — Part 1: Build Basic Containers and Images :
Your image (e.g. ToDoList:latest) should appear in the list.
Step 6 – Start Containers
Step 7 – Running Containers
Step 8 – Web Application
Procedure — Part 2 (Add health checkpoints & separate services) :
Step-1: Start full application stack
Command: i) docker-compose up --build
ii) docker-compose ps (or docker ps)
Verify: Check that all three containers (e.g., todo-app-frontend-1, todo-app-backend-1, todo-app-db-1) are Up and running.
Step-2: DB Initialization
The backend service (in index.js) is coded to automatically create the todos table upon its first successful connection to the PostgreSQL database.
SQL Schema (from index.js):
Code:
Step-1: Start full application stack
Command: i)
docker-compose up --buildii)docker-compose ps(ordocker ps)Verify: Check that all three containers (e.g.,
todo-app-frontend-1,todo-app-backend-1,todo-app-db-1) areUpand running.
Step-2: DB Initialization
The backend service (in
index.js) is coded to automatically create thetodostable upon its first successful connection to the PostgreSQL database.SQL Schema (from
index.js):
CREATE TABLE IF NOT EXISTS todos (
id SERIAL PRIMARY KEY,
text VARCHAR(255) NOT NULL,
completed BOOLEAN DEFAULT false
);
Step-3: Final Application Verification
Action: Open a web browser and navigate to
http://localhost:3000.Verify: Test the application by adding a new task, marking a task as complete, and deleting a task. This confirms the full frontend $\to$ backend $\to$ database communication is working correctly.
Procedure — Part 3 (Push image to Docker Hub) :
latest appears (size, digest, last pushed)Modifications done in the containers after building :
The modifications performed to create your final application include:
Environment variable support
Externalized the DATABASE_URL into the docker-compose.yml file, which is read by the Node.js backend to connect to the db service.
DB schema initialization
The backend's index.js file was coded to automatically run a CREATE TABLE IF NOT EXISTS todos... query on startup, ensuring the table is ready without manual setup.
Persistence
Mounted a named volume postgres_data:/var/lib/postgresql/data/ to persist the PostgreSQL database files on the host machine, ensuring tasks are saved even if the container restarts.
Service Networking
Defined a custom bridge network (todo-net) in Docker Compose to allow the frontend, backend, and db services to discover and communicate with each other using their service names.
Pushed images to Docker Hub
Tagged the two custom images (todo-app-fr, todo-app-b) with arjun100305/...:latest and pushed them to Docker Hub for reusability and deployment.
DockerHub link of your modified containers :
Docker Hub (Frontend image):
https://hub.docker.com/r/arjun100305/todo-app-frPull command:
docker pull arjun100305/todo-app-fr:latestDocker Hub (Backend image):
https://hub.docker.com/r/arjun100305/todo-app-bPull command:
docker pull arjun100305/todo-app-b:latest
Outcomes of the DA :
Two reproducible Docker images (
arjun100305/todo-app-frandarjun100305/todo-app-b) capable of running the 3-tier application anywhere with Docker.A multi-service
docker-compose.ymlfile that orchestrates the React frontend, Node.js backend, and PostgreSQL DB with persistent storage and service networking.An automated DB initialization workflow (via the Node.js app) for schema creation (the
todostable).Successful upload of both custom application images to Docker Hub, enabling sharing and redeployment.
Verified end-to-end flow: user inputs task in browser $\to$ React frontend calls backend API $\to$ Node.js backend writes to PostgreSQL DB.
Conclusion :
This project demonstrates best practices in containerizing a full-stack, 3-tier web application. It successfully decoupled the frontend, backend, and database into separate, communicating services, each within its own container. Packaging the entire stack into a Docker Compose file and publishing the custom images on Docker Hub enables reproducible deployment on any machine with a single command. The three-part approach (build frontend $\to$ build backend $\to$ compose all three) provides an extensible template for similar full-stack, microservice-style applications.
References :
-
Official Docker Documentation — https://docs.docker.com/
-
PostgreSQL Official Image — https://hub.docker.com/_/postgres
-
Node.js Official Image — https://hub.docker.com/_/node
-
Docker Hub Documentation — https://hub.docker.com
-
VIT SCOPE Course Material (Cloud Computing)
Comments
Post a Comment