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]>
389 lines
9.1 KiB
Markdown
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! ✨
|