Commit 17a2b46c authored by Markus Mößler's avatar Markus Mößler
Browse files

removed documentation from branch of react api

parent 5aff173e
Loading
Loading
Loading
Loading
+0 −493
Original line number Diff line number Diff line

# React-tornado-api (stage 01)

## Web Application Architecture Overview

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
    TornadoBackend -->|"SQL Queries (Port 5432)"| PostgreSQL
    TornadoBackend -->|Static Assets| Browser

    %% Docker Compose
    DockerCompose --- ReactFrontend
    DockerCompose --- TornadoBackend
    DockerCompose --- PostgreSQL
```

## Interaction Between Tornado Backend and React Frontend

In the react frontend I use the following react component (`./react-frontend/my-react-app/src/components/MessageComponent.js`),

```js
// ./react-frontend/my-react-app/src/components/MessageComponent.js

import React, { useEffect, useState } from "react";

const MessageComponent = () => {
  const [message, setMessage] = useState(""); // State to hold the fetched message
  const [error, setError] = useState(null); // State to hold any errors during fetch

  useEffect(() => {
    // Function to fetch the message from the backend
    const fetchMessage = async () => {
      try {
        const response = await fetch("/tornado-backend/message"); // Fetch from the proxy URL
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const data = await response.json(); // Parse JSON response
        setMessage(data.message); // Update the message state
      } catch (err) {
        setError(err.message); // Set the error state
      }
    };

    fetchMessage(); // Call the fetch function on component mount
  }, []); // Empty dependency array ensures this runs only once

  return (
    <div>
      <h1>Backend Message</h1>
      {error ? (
        <p style={{ color: "red" }}>Error: {error}</p> // Display error if any
      ) : (
        <p>{message || "Loading..."}</p> // Display message or a loading indicator
      )}
    </div>
  );
};

export default MessageComponent;
```

The component is routed to `(r"/tornado-backend/message", APIHandler),` in the tornado backend where the handler `APIHandler` looks like,

```py
class APIHandler(tornado.web.RequestHandler):
    def get(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.

---

### Detailed Explanation of the Interaction

#### 1. Frontend Component (`MessageComponent.js`)

- **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!"}
```
 No newline at end of file
+0 −150
Original line number Diff line number Diff line

# React Tornado API (Stage 01) - Overview

## File Structure

```bash
.
├── docker-compose.yml
├── documentation
├── react-frontend
│   ├── Dockerfile
│   └── my-react-app
│       ├── package.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
```

## Multi-Container Structure

```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"
```

## Visualization

```mermaid
graph TD
    subgraph Blog Service
        TornadoServer["Tornado Server"]
        Templates["HTML Templates (Jinja-like)"]
        StaticAssets["Static Assets (CSS/JS)"]
        APIEndpoint["API Endpoint /message"]
    end

    subgraph "React (frontend)<br>Service"
        ReactServer["React Server"]
        indexHtml["index.html"]
        favicon["favicon.ico"]
        manifestJson["manifest.json"]        
        indexJs["index.js"]
        indexCss["index.css"]
        AppJs["App.js"]
        AppCss["App.css"]
        MessageComponent["MessageComponent.js"]
    end

    subgraph Database Service 
        PostgreSQL["PostgreSQL Database"]
    end

    User["User"] -->|HTTP Requests<br>Port 8888| TornadoServer

    User["User"] -->|HTTP Requests<br>Port 3000| ReactServer
    ReactServer -->|Serves| indexHtml
    indexHtml -->|Mounts React App| indexJs
    indexJs -->|Imports| indexCss
    indexJs -->|Imports| AppJs
    AppJs -->|Imports| AppCss
    AppJs -->|Imports| MessageComponent
    indexHtml -->|References| favicon
    indexHtml -->|References| manifestJson

    MessageComponent -->|Fetches API<br>via Proxy| TornadoServer
    TornadoServer -->|Renders| Templates
    TornadoServer -->|Serves| StaticAssets
    TornadoServer -->|Serves API<br>/message| APIEndpoint
    TornadoServer -->|Async Queries<br>Port 5432| PostgreSQL
```

Note, to use react live reloading during development specify the proxy in the `./react-frontend/my-react-app/package.json` file, i.e., use, e.g.,

```json
  "name": "app",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://tornado-backend:8888",
//   ...
```
 No newline at end of file