8.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
A real-time trivia game web application with Flask backend and React frontend, featuring WebSocket support for live score updates and question displays. The app supports creating question banks, setting up games with teams, and running live trivia games with separate contestant (TV display) and admin control views.
Tech Stack
Backend:
- Flask 3.0+ with Flask-SocketIO for WebSocket support
- SQLAlchemy ORM with Flask-Migrate for database migrations
- SQLite database (located at
backend/instance/trivia.db) - Eventlet for async/WebSocket server
Frontend:
- React 19 with React Router 7
- Vite for build tooling and dev server
- Socket.IO client for WebSocket communication
- Axios for HTTP requests
- Note: Frontend source is nested at
frontend/frontend/(double directory structure)
Development Commands
Backend Setup and Running
# Install Python dependencies
uv sync
# Initialize database (first time only)
export FLASK_APP=backend.app:create_app
uv run flask db init
# Create and apply migrations
uv run flask db migrate -m "Description of changes"
uv run flask db upgrade
# Run backend server (use PORT=5001 to avoid macOS AirPlay conflict on 5000)
PORT=5001 uv run python main.py
Frontend Setup and Running
# Install frontend dependencies
cd frontend/frontend # Note the nested directory
npm install
# Run frontend dev server (with proxy to backend)
npm run dev # Runs on port 3000
# Build for production (outputs to backend/static/)
npm run build
Database Migrations
When you modify models in backend/models.py:
export FLASK_APP=backend.app:create_app
uv run flask db migrate -m "Description of changes"
uv run flask db upgrade
Docker Development (Recommended)
Docker Compose provides the easiest development setup with automatic hot reload for both frontend and backend:
# Start all services (backend + frontend with hot reload)
docker compose up
# Start in detached mode
docker compose up -d
# View logs
docker compose logs -f
# Stop all services
docker compose down
# Rebuild images after dependency changes
docker compose up --build
# Run database migrations inside container
docker compose exec backend uv run flask db migrate -m "Description"
docker compose exec backend uv run flask db upgrade
Services:
- Backend: http://localhost:5001 (Flask + SocketIO)
- Frontend: http://localhost:3000 (Vite dev server)
Hot Reload:
- Backend: Changes to
backend/,main.py, andmigrations/automatically reload Flask - Frontend: Changes to
src/,public/, and config files automatically reload Vite - Database and uploaded images persist in volumes
Architecture:
Dockerfile.backend: Python 3.14 with uv, Flask in debug modeDockerfile.frontend: Node 18 with Vite dev serverdocker-compose.yml: Orchestrates both services with volume mounts for hot reload- Named volume for
node_modulesto avoid host/container conflicts - Health check on backend before starting frontend
Architecture
Application Factory Pattern
The Flask app uses an application factory (backend/app.py:create_app()) which:
- Initializes extensions (SQLAlchemy, Flask-Migrate, Flask-SocketIO, CORS)
- Registers blueprints for routes
- Serves React frontend in production from
backend/static/ - Configured via
backend/config.pywith environment-based settings
Database Models (backend/models.py)
Core entities:
- Question: Stores trivia questions (text or image type) with answers and optional categories
- Game: Represents a trivia game session with
is_activeflag andcurrent_question_index - GameQuestion: Junction table linking questions to games with ordering
- Team: Teams participating in a game
- Score: Tracks points awarded per team per question (composite unique constraint on team_id + question_index)
- Category: Optional categorization for questions
Key relationships:
- Games can only be active one at a time (
Game.get_active()class method) - Teams calculate
total_scoreas a property by summing all Score records - Questions can be reused across multiple games via GameQuestion junction table
WebSocket Architecture (backend/sockets/events.py, backend/services/game_service.py)
Room-based design:
- Each game has two rooms:
game_{id}_contestantandgame_{id}_admin - Contestants join the contestant room and see questions WITHOUT answers
- Admin joins the admin room and sees questions WITH answers
- Score updates broadcast to both rooms
Key events:
join_game/leave_game: Client joins/leaves game rooms with role (contestant/admin)game_started: Emitted when game startsquestion_changed: Sent to contestant room (no answer)question_with_answer: Sent to admin room (with answer)score_updated: Broadcasts score changes to both roomsanswer_visibility_changed: Toggles answer display on contestant screen
Game flow:
- Admin starts game via
/api/admin/game/<id>/start→ broadcasts first question - Admin navigates questions with
/nextand/prev→ broadcasts to all clients - Admin awards points via
/award→ creates/updates Score records → broadcasts to all - Optional answer reveal to contestants via
/toggle-answer
API Routes
Routes are organized into blueprints:
backend/routes/questions.py: Question CRUD (/api/questions)backend/routes/games.py: Game management (/api/games)backend/routes/teams.py: Team management (/api/teams)backend/routes/admin.py: Game control endpoints (/api/admin/game/<id>/...)backend/routes/categories.py: Category management (/api/categories)
Admin endpoints use game_service.py functions which handle both database updates AND WebSocket broadcasts.
Frontend Build Configuration
Development:
- Vite dev server runs on port 3000
- Proxies
/apiand/socket.iorequests to Flask backend on port 5001 - Configuration in
frontend/frontend/vite.config.js
Production:
npm run buildoutputs tobackend/static/(configured viabuild.outDir)- Flask serves the built React app and handles client-side routing
- All non-API routes return
index.htmlfor React Router
Image Handling
- Images uploaded to
backend/static/images/viabackend/services/image_service.py - Image questions store
image_pathin the Question model - Images served as static files by Flask in production
Common Development Patterns
Adding a New Model Field
- Modify the model in
backend/models.py - Update the model's
to_dict()method if the field should be serialized - Create migration:
uv run flask db migrate -m "Add field_name to Model" - Apply migration:
uv run flask db upgrade
Adding a New WebSocket Event
- Define event handler in
backend/sockets/events.pyusing@socketio.on('event_name') - Use
emit()to send responses orjoin_room()/leave_room()for room management - To broadcast to specific rooms, use
emit('event', data, room='room_name') - Update frontend Socket.IO hook to listen for the new event
Adding a New API Endpoint
- Add route function to appropriate blueprint in
backend/routes/ - Use
db.session.commit()for database changes with try/except for rollback - Return JSON responses with appropriate status codes
- For admin actions affecting game state, use functions from
game_service.pyto ensure WebSocket broadcasts
Important Notes
- Port 5000 conflict: macOS AirPlay Receiver uses port 5000 by default. Use
PORT=5001when running the backend. - Nested frontend directory: Frontend source code is at
frontend/frontend/, notfrontend/src/ - Single active game: Only one game can be active at a time (enforced in
start_game()) - Score uniqueness: A team can only have one score per question (enforced by database constraint)
- WebSocket server: Must use
socketio.run(app)notapp.run()for WebSocket support (configured inmain.py) - CORS: Configured to allow all origins in development via
CORS_ORIGINS=*