I started with the replication of the [tornado blog demo](https://github.com/tornadoweb/tornado/blob/master/demos/blog/blog.py) in branch `tornado-blog-demo`.
In the next step I added a react frontend to fetch a simple message from the tornado backend.
The file directory of this stage looks like,
```bash
markus@markus-20eqs20d00:~/gitlab/aidaho-tinkering-club-web-app$ tree .-I"node_modules"
.
├── docker-compose.yml
├── documentation
│ ├── react_frontend_stage_01.md
│ └── tornado_backend_stage_01.md
├── react-frontend
│ ├── Dockerfile
│ └── my-react-app
│ ├── package.json
│ ├── package-lock.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── README.md
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ │ ├── HomeComponent.js
│ │ └── MessageComponent.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── reportWebVitals.js
│ └── setupTests.js
├── README.md
└── tornado-backend
├── blog.py
├── docker-compose.yml
├── Dockerfile
├── README.md
├── requirements.txt
├── schema.sql
├── static
│ └── blog.css
└── templates
├── archive.html
├── base.html
├── compose.html
├── create_author.html
├── entry.html
├── feed.xml
├── home.html
├── login.html
└── modules
└── entry.html
```
The docker compose file for complete web application (`docker-compose.yml`) looks like,
```yml
# .
services:
react-frontend:
build:
context:./react-frontend
volumes:
-./react-frontend/my-react-app:/app
ports:
-"3000:3000"
stdin_open:true
tty:true
environment:
-CHOKIDAR_USEPOLLING=true# To support live reloading on mounted volumes
command:["npm","start"]
depends_on:
-tornado-backend
tornado-backend:
build:./tornado-backend
links:
-postgres
ports:
-"8888:8888"
command:--db_host=postgres
postgres:
image:postgres:10.3
environment:
POSTGRES_USER:blog
POSTGRES_PASSWORD:blog
POSTGRES_DB:blog
ports:
-"5432:5432"
```
The docker file for the tornado backend (`./tornado-backend/Dockerfile`) looks like,
```Dockerfile
# ./tornado-backend/Dockerfile
FROM python:3.7
EXPOSE 8888
RUN mkdir-p /usr/src/app
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install--no-cache-dir-r requirements.txt
COPY . .
ENTRYPOINT ["python3", "blog.py"]
```
The docker file for the reat frontend (`./react-frontend/Dockerfile`) looks like,
```Dockerfile
# ./react-frontend/Dockerfile
FROM node:16
WORKDIR /app
# Copy package.json and package-lock.json
COPY ./my-react-app/package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Create .cache directory and set permissions
RUN mkdir-p /app/node_modules/.cache &&chmod-R 777 /app/node_modules/.cache
# Set permissions for the entire application directory
RUN chmod-R 777 /app
# Start the React development server
CMD ["npm", "start"]
```
The package json file for the react frontend (`./react-frontend/my-react-app/package.json`) looks like,
```json
{
"name":"app",
"version":"0.1.0",
"private":true,
"proxy":"http://tornado-backend:8888",
"dependencies":{
"cra-template":"1.2.0",
"react":"^19.0.0",
"react-dom":"^19.0.0",
"react-router-dom":"^7.0.2",
"react-scripts":"5.0.1",
"web-vitals":"^4.2.4"
},
"scripts":{
"start":"react-scripts start",
"build":"react-scripts build",
"test":"react-scripts test",
"eject":"react-scripts eject"
},
"eslintConfig":{
"extends":[
"react-app",
"react-app/jest"
]
},
"browserslist":{
"production":[
">0.2%",
"not dead",
"not op_mini all"
],
"development":[
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
```
## Explanation: Web Application Architecture Overview
### Current Stage Overview
The web application consists of three primary components:
1.**Frontend (React)**
2.**Backend (Tornado)**
3.**Database (PostgreSQL)**
These components are containerized using Docker and orchestrated with Docker Compose.
---
### 1. React Frontend
-**Technology:** React.js (served via Node.js)
-**Purpose:** Serves as the user interface (UI) for the application.
-**Key Features:**
- Contains a React application located in the `my-react-app` directory.
- Communicates with the Tornado backend using HTTP requests.
- A `proxy` is configured in `package.json` to route API requests to `http://tornado-backend:8888`.
- Supports hot reloading in development through the `CHOKIDAR_USEPOLLING` environment variable and volume mounting.
- The React application code is modular and organized with reusable components (`HomeComponent.js`, `MessageComponent.js`, etc.).
-**Containerization:**
- Uses a Node.js base image (`node:16`) in its Dockerfile.
- Starts the React development server (`npm start`), exposing port `3000`.
- Maps the `my-react-app` directory to the container to reflect live code changes.
-**Interaction:**
- Acts as the user-facing layer.
- Sends requests to the Tornado backend for dynamic data, such as messages.
---
### 2. Tornado Backend
-**Technology:** Tornado Web Framework (Python)
-**Purpose:** Acts as the API server and business logic layer.
-**Key Features:**
- Based on the Tornado blog demo.
- Handles HTTP requests and serves API endpoints.
- Implements routing and connects to the PostgreSQL database for data storage and retrieval.
- Provides static assets like CSS and templates for rendering if needed (e.g., for SSR).
-**Containerization:**
- Uses a Python base image (`python:3.7`) in its Dockerfile.
- Installs dependencies listed in `requirements.txt` using pip.
- Exposes port `8888` for the Tornado server.
- Starts the application by running `blog.py`.
-**Interaction:**
- Acts as the backend for the React frontend.
- Fetches and serves data from the PostgreSQL database.
- Directly communicates with the PostgreSQL container using the hostname `postgres`.
---
### 3. PostgreSQL Database
-**Technology:** PostgreSQL (10.3)
-**Purpose:** Stores persistent data for the application.
-**Key Features:**
- User credentials, posts, and other application-specific data are stored in a database.
- A schema (`schema.sql`) defines the structure of the database tables.
- Configuration is managed via environment variables (`POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`).
-**Containerization:**
- Uses the official PostgreSQL image (`postgres:10.3`).
- Exposes port `5432` for database connections.
- Stores database files in a Docker volume for persistent storage across container restarts.
-**Interaction:**
- Accepts SQL queries from the Tornado backend for data manipulation.
- Exposed to the Tornado backend via the Docker network, allowing seamless communication.
---
### Docker Compose Configuration
The application architecture is orchestrated using Docker Compose, enabling seamless setup and networking of all components:
#### 1. Networking
- All services (`react-frontend`, `tornado-backend`, `postgres`) are connected through a shared Docker network.
- Each service can communicate with others via their container names (`tornado-backend` and `postgres`).
#### 2. Dependencies
- The `react-frontend` service depends on the `tornado-backend`, ensuring the backend is ready before the frontend starts.
- The `tornado-backend` service links to the `postgres` service for database interactions.
#### 3. Ports
- React frontend: Exposes port `3000` for local development.
- Tornado backend: Exposes port `8888` for API requests.
- PostgreSQL: Exposes port `5432` for database connections.
---
### Workflow of the Application
1.**Frontend Interaction:**
- A user accesses the React frontend through `http://localhost:3000`.
- React components send API requests (e.g., fetch a message) to the Tornado backend.
2.**Backend Processing:**
- Tornado receives API requests on `http://localhost:8888`.
- Processes requests, potentially interacting with the PostgreSQL database for persistent data.
3.**Database Querying:**
- Tornado issues SQL queries to the PostgreSQL database for data storage or retrieval.
4.**Response Flow:**
- Data retrieved from PostgreSQL is processed by Tornado and sent back to the React frontend.
- The React frontend updates the UI to display the retrieved data.
---
### Current Capabilities
-**Development Environment:** A fully functional development setup for both frontend and backend.
-**Data Flow:** Frontend can fetch data from the backend and display it to users.
-**Persistence:** Database integration ensures data can be stored and retrieved as needed.
-**Modular Design:** Separate containers for each component enable scalable and maintainable architecture.
### Visualization
```mermaid
graph TD
ReactFrontend["React Frontend (Node.js)"]
TornadoBackend["Tornado Backend (Python)"]
PostgreSQL["PostgreSQL Database"]
Browser["User's Browser"]
DockerCompose["Docker Compose Network"]
%% Connections
Browser -->|"HTTP (Port 3000)"| ReactFrontend
ReactFrontend -->|"HTTP API (Port 8888)"| TornadoBackend
The component is routed to `(r"/tornado-backend/message", APIHandler),` in the tornado backend where the handler `APIHandler` looks like,
```py
classAPIHandler(tornado.web.RequestHandler):
defget(self):
self.write({"message":"Hello from Tornado Backend!"})
```
Note, in the react package json file I use `"proxy": "http://tornado-backend:8888",` for the react frontend.
How would you call such an interaction between the tornado backend and the react frontend? Can you explain this interaction?
## Explanation: Interaction Between Tornado Backend and React Frontend
### Overview
The interaction between the **React frontend** and the **Tornado backend** is an example of **Client-Server API communication**. Specifically, it involves HTTP API requests, with the React development server proxying these requests to the Tornado backend.
-**Purpose:** The React component fetches a message from the Tornado backend when it mounts.
-**Key Mechanism:**
- The `fetch` API is used to send an HTTP `GET` request to `/tornado-backend/message`.
- The request is proxied through the development server (`proxy` set in `package.json` as `"http://tornado-backend:8888"`), so the actual request URL becomes `http://tornado-backend:8888/tornado-backend/message`.
-**Workflow:**
1. When the `MessageComponent` mounts, the `useEffect` hook triggers the `fetchMessage` function.
2.`fetchMessage` sends an HTTP request to the backend.
3. If the request is successful, the response is parsed as JSON and the message is stored in the React state (`setMessage`).
4. If an error occurs (e.g., network issues or a non-2xx status code), the error is caught and stored in the state (`setError`).
---
#### 2. Backend Handler (`APIHandler`)
-**Purpose:** Serve a simple JSON response containing a message.
-**Key Mechanism:**
- The Tornado backend listens for incoming HTTP requests on the route `/tornado-backend/message`.
- The `APIHandler` is mapped to this route using Tornado's routing mechanism.
-**Workflow:**
1. When the backend receives the `GET` request at `/tornado-backend/message`, Tornado invokes the `get` method of the `APIHandler`.
2. Inside the `get` method, a JSON response is created with the message `{"message": "Hello from Tornado Backend!"}`.
3. The `self.write` method sends this JSON response back to the frontend.
---
#### 3. Proxying via React Development Server
-**Purpose:** Simplify the communication during development by avoiding CORS (Cross-Origin Resource Sharing) issues.
-**Key Mechanism:**
- The `proxy` field in `package.json` routes all unrecognized API requests from the React frontend to the Tornado backend (`http://tornado-backend:8888`).
-**Workflow:**
1. The browser sends the API request to the React development server at `/tornado-backend/message`.
2. The development server recognizes that `/tornado-backend/message` is not a frontend route.
3. It forwards the request to the Tornado backend using the specified proxy URL.
---
#### 4. End-to-End Data Flow
1.**Frontend Initiates the Request:**
- The React `MessageComponent` sends a `GET` request to `/tornado-backend/message`.
2.**Backend Processes the Request:**
- The Tornado backend handles the request, generates the JSON response, and sends it back to the frontend.
3.**Frontend Receives the Response:**
- The React frontend parses the JSON response.
- The `message` is displayed in the component's UI.
---
### Key Benefits of This Approach
-**Separation of Concerns:**
- The React frontend is responsible for UI rendering.
- The Tornado backend handles business logic and data delivery.
-**Development Convenience:**
- Using the `proxy` setting simplifies backend communication during development and avoids CORS issues.
-**Scalability:**
- This architecture allows the frontend and backend to scale independently.
---
### Summary
This interaction demonstrates a typical **Client-Server API communication pattern**:
- The **frontend** fetches dynamic data from the backend using HTTP API requests.
- The **backend** processes these requests and provides the required data or functionality.
- The development proxy in React facilitates seamless communication without cross-origin issues during local development.
### Visualization
```mermaid
sequenceDiagram
participant ReactFrontend as React Frontend (Client)
participant TornadoBackend as Tornado Backend (API Server)
ReactFrontend->>TornadoBackend: HTTP GET /tornado-backend/message
TornadoBackend-->>ReactFrontend: JSON Response {"message": "Hello from Tornado Backend!"}