# Routers Directory This directory contains **FastAPI route handlers** following the Model/Service Architecture pattern. Routes are thin controllers that handle HTTP concerns and delegate business logic to services. ## Architecture Pattern ### Route Responsibilities - **HTTP Handling**: Request/response processing, status codes - **Input Validation**: Request data validation and parsing - **Service Delegation**: Delegate business logic to services - **Response Formatting**: Transform service results for HTTP responses ### Design Principles - **Thin Controllers**: Minimal logic, delegate to services - **Dependency Injection**: Use FastAPI `Depends()` for services - **Error Handling**: Transform service exceptions to HTTP errors - **Documentation**: FastAPI auto-generates OpenAPI documentation ## Current Files ### `auth.py` Authentication routes: - **Discord OAuth**: Login redirect and callback handling - **Session Management**: Login/logout functionality - **Uses**: `AuthService` for business logic ### `games.py` Game-related endpoints: - **Game Creation**: Start new games - **Game Retrieval**: Get game details and listings - **Uses**: `GameService` for business logic ### `api.py` JSON API endpoints for HTMX: - **Live Updates**: Scoreboard and game state - **Player Actions**: Execute gameplay actions - **Uses**: `GameplayService` for real-time updates ### `pages.py` HTML page routes with Jinja2 templates: - **Home Page**: Main application entry point - **Game Interface**: Live game viewing - **Template Rendering**: Server-side HTML generation ## Route Pattern Example ```python from fastapi import APIRouter, HTTPException from app.services.service_container import GameServiceDep router = APIRouter() @router.post("/games/start") async def start_game( request: StartGameRequest, game_service: GameServiceDep ): """Start a new game - delegates to GameService.""" try: game = await game_service.create_game( away_team_id=request.away_team_id, home_team_id=request.home_team_id ) return {"game_id": game.id, "status": "created"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) ``` ## Service Integration Routes use dependency injection to access services: ```python # Service dependencies from service_container.py GameServiceDep = Annotated[GameService, Depends(get_game_service)] UserServiceDep = Annotated[UserService, Depends(get_user_service)] AuthServiceDep = Annotated[AuthService, Depends(get_auth_service)] ``` ## Route Organization ### `/auth` - Authentication - `GET /auth/login` - Discord OAuth redirect - `GET /auth/callback` - OAuth callback handler - `POST /auth/logout` - Logout and session cleanup ### `/games` - Game Management - `POST /games/start` - Create new game - `GET /games/{game_id}` - Get game details - `GET /games/` - List active games ### `/api` - HTMX JSON API - `GET /api/scoreboard/{game_id}` - Live scoreboard data - `POST /api/play` - Execute gameplay action ### `/` - HTML Pages - `GET /` - Home page - `GET /game/{game_id}` - Game interface page ## Error Handling Routes should transform service exceptions to appropriate HTTP responses: ```python try: result = await service.do_something() return result except NotFoundException as e: raise HTTPException(status_code=404, detail=str(e)) except ValidationError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Unexpected error: {e}") raise HTTPException(status_code=500, detail="Internal server error") ``` ## Testing Routes Routes should be tested with service dependencies mocked: ```python from fastapi.testclient import TestClient from unittest.mock import Mock def test_start_game(mock_game_service): client = TestClient(app) # Mock service response mock_game_service.create_game.return_value = Game(id=1) response = client.post("/games/start", json={ "away_team_id": 1, "home_team_id": 2 }) assert response.status_code == 200 assert response.json()["game_id"] == 1 ```