Files
payguard-poc/NETWORKING_GUIDE.md
T
John Patrick TeruelandCopilot 982b70a2ca 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 <[email protected]>
2026-04-24 20:05:21 +08:00

389 lines
9.1 KiB
Markdown

# 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! ✨