From 982b70a2caa8b2ce62989b5b1979833ef6bb39ee Mon Sep 17 00:00:00 2001 From: John Patrick Teruel Date: Fri, 24 Apr 2026 20:05:21 +0800 Subject: [PATCH] Add comprehensive Docker networking documentation Created NETWORKING_GUIDE.md explaining the host.docker.internal solution: - Problem: Docker frontend can't reach localhost:8000 on host - Solution: Use host.docker.internal for automatic DNS mapping - Implementation details for App.tsx, docker-compose.yml, .env - Step-by-step usage guide (3-terminal setup) - Verification procedures and troubleshooting - Platform-specific notes (macOS, Windows, Linux) - Development workflow (Docker vs npm dev) - Technical background and alternatives - Advanced configuration options - Quick reference cheat sheet Benefits: - Frontend automatically selects correct API URL - No manual configuration needed - Works on Docker Desktop (macOS/Windows) - Easy development workflow - Production-ready setup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- NETWORKING_GUIDE.md | 388 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 NETWORKING_GUIDE.md diff --git a/NETWORKING_GUIDE.md b/NETWORKING_GUIDE.md new file mode 100644 index 0000000..6f6b619 --- /dev/null +++ b/NETWORKING_GUIDE.md @@ -0,0 +1,388 @@ +# Docker Frontend-to-Backend Networking Guide + +## Problem Solved ✅ + +The React frontend running inside a Docker container needed to connect to the FastAPI backend running on the host machine at `localhost:8000`. + +**The Challenge**: Inside a Docker container, `localhost` refers to the container itself, not your host machine. + +**The Solution**: Use `host.docker.internal` - Docker Desktop's special DNS name that automatically maps to the host machine. + +--- + +## How It Works + +### Network Architecture + +``` +Your Host Machine Docker Container +┌──────────────────────────┐ ┌──────────────────────┐ +│ │ │ │ +│ FastAPI │◄─────────────┤ React Frontend │ +│ localhost:8000 │ │ host.docker │ +│ │ │ .internal:8000 │ +│ Consumer │ │ │ +│ (Kafka listener) │ └──────────────────────┘ +│ │ +└──────────────────────────┘ +``` + +### The Magic + +Docker Desktop automatically provides `host.docker.internal` as a DNS entry that resolves to your host machine's `localhost`. This allows containers to reach services running on the host. + +--- + +## Implementation Details + +### 1. Frontend Code (App.tsx) + +The frontend intelligently selects the API URL based on environment: + +```typescript +const API_BASE_URL = process.env.NODE_ENV === 'production' + ? 'http://host.docker.internal:8000' // Docker containers + : 'http://localhost:8000'; // Local development +``` + +**Benefits:** +- `npm run dev` (development mode) → uses `localhost:8000` ✓ +- Docker (production mode) → uses `host.docker.internal:8000` ✓ +- No hardcoding, automatic detection ✓ + +### 2. Docker Configuration (docker-compose.yml) + +Frontend service explicitly sets the environment: + +```yaml +frontend: + build: + context: ./frontend + dockerfile: Dockerfile + environment: + - NODE_ENV=production # Triggers production API URL + - VITE_API_URL=... # Optional override +``` + +### 3. Environment Configuration (.env.example) + +```bash +# Frontend can optionally override with environment variable +VITE_API_URL=http://host.docker.internal:8000 +NODE_ENV=production +``` + +--- + +## How to Use + +### Standard Setup (3 Terminals) + +**Terminal 1:** Start infrastructure + frontend in Docker +```bash +docker compose up +``` + +Output shows: +``` +frontend | ✓ Ready on http://0.0.0.0:3000 +kafka | [Ready] +redis | [Ready] +zookeeper | [Ready] +``` + +**Terminal 2:** Start API on your machine +```bash +source venv/bin/activate +uvicorn api.main:app --reload --port 8000 +``` + +Output shows: +``` +Uvicorn running on http://127.0.0.1:8000 +``` + +**Terminal 3:** Start consumer on your machine +```bash +cd consumer && python pipeline.py +``` + +Output shows: +``` +Connected to Kafka broker at kafka:9092 +Consumer ready... +``` + +**Browser:** Open http://localhost:3000 + +The frontend automatically connects to your API at `http://host.docker.internal:8000` and works seamlessly! + +--- + +## Verification + +### Check Frontend Can Reach API + +```bash +# From inside the Docker container +docker exec $(docker ps -q -f name=payguard-frontend) \ + wget --spider -q http://host.docker.internal:8000 + +# Should complete without error (exit code 0) +``` + +### Manual Test + +```bash +# Start API first +uvicorn api.main:app --reload + +# Then in another terminal, test the endpoint +curl http://localhost:8000/health +# Should return: {"status": "healthy", ...} +``` + +### From Frontend + +The frontend automatically tests connectivity: + +```typescript +const generateToken = async () => { + try { + const response = await axios.post(`${API_BASE_URL}/token`, { + user_id: auth.userId, + }); + // Connection succeeded! + setAuth(prev => ({...prev, token: ...})); + } catch (error) { + // Connection failed + setError("Could not connect to API at " + API_BASE_URL); + } +}; +``` + +--- + +## Platform Compatibility + +| Platform | Support | Notes | +|----------|---------|-------| +| **macOS** | ✅ Full | Docker Desktop includes host.docker.internal | +| **Windows** | ✅ Full | Docker Desktop includes host.docker.internal | +| **Linux** | ⚠️ Manual | May need configuration (see below) | + +### Linux-Specific Setup + +On Linux, `host.docker.internal` is not automatically available. Options: + +**Option 1: Use Docker gateway (Recommended)** +```yaml +frontend: + environment: + - VITE_API_URL=http://172.17.0.1:8000 # Default Docker gateway +``` + +**Option 2: Pass --add-host flag** +```bash +docker compose run --add-host=host.docker.internal:host-gateway frontend +``` + +**Option 3: Use network_mode: host** +```yaml +frontend: + network_mode: "host" + # Then remove ports and depends_on conditions +``` + +--- + +## Development Workflow + +### Running Frontend Locally (npm dev mode) + +If you want to develop the frontend with hot-reloading: + +```bash +# Stop the Docker frontend +docker compose stop frontend + +# Start the local dev server +cd frontend +npm run dev + +# This uses localhost:8000 automatically +``` + +The dev server will hot-reload as you edit code. + +### Running Frontend in Docker (production build) + +```bash +docker compose up frontend + +# Uses host.docker.internal:8000 +# Production-optimized build, no hot-reload +``` + +--- + +## Troubleshooting + +### "Cannot connect to API" Error + +**Check 1:** Is the API running? +```bash +curl http://localhost:8000/health +``` + +**Check 2:** Is it listening on port 8000? +```bash +lsof -i :8000 +# Should show: uvicorn LISTEN +``` + +**Check 3:** Is Docker container healthy? +```bash +docker compose ps +# Should show: frontend Up (healthy) +``` + +**Check 4:** Test from inside container +```bash +docker exec $(docker ps -q -f name=payguard-frontend) \ + wget --verbose http://host.docker.internal:8000 +# Should succeed with 200 status +``` + +### API is running but frontend still fails + +Try these in order: + +1. **Rebuild frontend** + ```bash + docker compose up --build frontend + ``` + +2. **Check logs** + ```bash + docker compose logs -f frontend + # Look for connection errors + ``` + +3. **On Linux, use manual gateway** + ```bash + # Edit docker-compose.yml + # Change VITE_API_URL to http://172.17.0.1:8000 + docker compose up --build + ``` + +4. **Hardcode the URL (Debug only)** + ```bash + # Edit frontend/.env file + VITE_API_URL=http://host.docker.internal:8000 + docker compose up --build + ``` + +--- + +## Advanced Configuration + +### Custom API URL + +If you need a different API location: + +**Option 1: Environment variable** +```bash +# Create frontend/.env (or .env.local) +VITE_API_URL=http://custom-api.example.com:8000 + +# Then restart +docker compose up --build frontend +``` + +**Option 2: Modify docker-compose.yml** +```yaml +frontend: + environment: + - VITE_API_URL=http://api.example.com:8000 +``` + +**Option 3: Modify App.tsx** (hardcoding - not recommended) +```typescript +const API_BASE_URL = 'http://your-api:8000'; +``` + +### Multi-Machine Setup + +If API is on a different machine: + +```yaml +frontend: + environment: + - VITE_API_URL=http://192.168.1.100:8000 # Replace with your API machine +``` + +--- + +## Technical Background + +### Why host.docker.internal Works + +Docker Desktop runs a lightweight Linux VM that contains your containers. Docker Desktop includes special DNS resolution: + +- **In containers**: `host.docker.internal` → `192.168.65.2` (Docker Desktop VM gateway) +- **In Docker Desktop VM**: `192.168.65.2` → your host machine's `localhost` + +This creates a transparent bridge from containers to the host. + +### Alternative Approaches (Not Used Here) + +1. **Docker network with backend container** - Would require FastAPI in Docker too +2. **Use external network** - More complex setup +3. **Use host network mode** - Breaks container isolation +4. **API in Docker, frontend on host** - Opposite of current setup + +The current approach (`host.docker.internal`) is the best balance for development: +- ✅ Frontend uses production Docker build +- ✅ API/Consumer use local development with hot-reloading +- ✅ Easy to iterate and debug +- ✅ No complex networking configuration + +--- + +## Summary + +| Aspect | Solution | +|--------|----------| +| **Frontend location** | Docker container (port 3000) | +| **API location** | Your host machine (port 8000) | +| **Connection method** | `host.docker.internal:8000` | +| **Auto-detection** | Yes (based on NODE_ENV) | +| **Configuration needed** | None (automatic!) | +| **Works on macOS/Windows** | Yes ✓ | +| **Works on Linux** | Yes (with one env var) | + +--- + +## Quick Reference + +```bash +# Everything you need + +# Terminal 1: Start Docker infrastructure + frontend +docker compose up + +# Terminal 2: Start API +source venv/bin/activate +uvicorn api.main:app --reload + +# Terminal 3: Start consumer +cd consumer && python pipeline.py + +# Browser: Open this URL +http://localhost:3000 +``` + +Frontend automatically finds API at `host.docker.internal:8000`. + +No additional configuration needed! ✨