- TypeScript 61.2%
- Python 37.6%
- CSS 1%
- HTML 0.1%
- JavaScript 0.1%
| .forgejo/workflows | ||
| backend | ||
| frontend | ||
| .env.example | ||
| .gitignore | ||
| .gitleaks.toml | ||
| docker-compose.yml | ||
| LICENSE | ||
| plan.md | ||
| README.md | ||
| RELEASE.md | ||
| SECURITY.md | ||
NBA Stats
A mobile-friendly NBA stats web app built as a monorepo with a React/Vite frontend and a FastAPI backend.
Live site: https://nba.evileko.dev
The frontend provides live scores, standings, player/team views, shot charts, playoff views, comparisons, trends, favorites, and betting odds. The backend handles API aggregation, disk-backed caching, rate limiting, live score streaming, and fallbacks for data that is not available on the free balldontlie tier.
Stack
Frontend
- React 19 + TypeScript
- Vite
- TanStack React Query
- React Router
- Zustand
- Recharts
- Framer Motion
- vite-plugin-pwa
Backend
- FastAPI
- Python 3.11+
- SQLite (default disk cache backend) or Redis (optional)
nba_apihttpx- Server-Sent Events (SSE)
Data sources
- balldontlie for games, players, and teams
- nba_api for standings and shot chart data
- The Odds API for pregame odds snapshots
Key features
- Live scoreboard with SSE updates
- Game detail pages with play-by-play and full-court shot chart playback
- Conference standings and playoff/play-in views
- Player and team detail pages
- Shot charts and heat maps
- Player comparison and trend views
- Favorites persisted on the client
- Mobile-first layout with bottom tab navigation and desktop sidebar
- PWA support for installable/mobile use
Repository layout
.
├── backend/ # FastAPI app, services, routers, tests
├── frontend/ # React/Vite app
├── docker-compose.yml
└── README.md
Architecture
External APIs
├─ balldontlie
├─ nba_api
└─ The Odds API
↓
FastAPI backend
├─ routers in backend/app/routers
├─ services in backend/app/services
├─ SQLite disk cache by default (Redis optional)
└─ SSE live score stream
↓
React frontend
├─ React Query for server state
├─ Zustand for favorites/local UI state
└─ Vite proxy in dev, static files served by FastAPI in prod
Backend routes
All backend routes are prefixed with /api.
/api/health/api/games/api/games/live/api/games/{game_id}/api/games/{game_id}/boxscore/api/games/{game_id}/plays/api/players/api/players/{player_id}and related subroutes/api/teams/{team_id}and related subroutes/api/standings/api/standings/playoffs/api/standings/h2h/api/stats/leaders/api/odds/api/shot-charts/game/api/shot-charts/{player_id}
Prerequisites
- Node.js and npm
- Python 3.11+
- Redis only if you choose
CACHE_BACKEND=redis
Environment
Create backend/.env from the project example values.
Example variables from .env.example:
BALLDONTLIE_API_KEY=your_key_here
THE_ODDS_API_KEY=your_key_here
ODDS_DEFAULT_BOOKMAKER=draftkings
ODDS_FALLBACK_BOOKMAKERS=fanduel,betmgm,caesars
CACHE_BACKEND=sqlite
CACHE_DB_PATH=data/cache.sqlite3
REDIS_URL=redis://localhost:6379
LIVE_SCORES_POLL_INTERVAL_SECONDS=30
LIVE_SCORES_CACHE_TTL_SECONDS=90
CORS_ORIGINS=http://localhost:5173
ENV=development
VITE_API_BASE_URL=http://localhost:8000/api
Install
Frontend
cd frontend
npm install
Backend
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Run locally
For the default low-RAM setup, no Redis is required: cached API responses are stored in backend/data/cache.sqlite3 on disk.
If you prefer Redis, set CACHE_BACKEND=redis and start Redis:
redis-server --daemonize yes
Or with Docker Compose:
docker compose up -d redis
Start the backend:
cd backend
source .venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
Start the frontend:
cd frontend
npm run dev -- --host 0.0.0.0
Build and checks
Frontend build
cd frontend
npm run build
Frontend lint
cd frontend
npm run lint
Backend lint
cd backend
source .venv/bin/activate
ruff check app/
Backend tests
cd backend
source .venv/bin/activate
python -m unittest discover -s tests
Production notes
backend/app/main.pymountsfrontend/distwhen it exists and falls back toindex.htmlfor SPA routing- for the smallest host footprint, prefer a single backend worker/process with the default SQLite cache backend
- this repository does not include personal deployment scripts or tunnel config; deploy it however you prefer
Notes
- The backend stores cache entries on disk by default, so historical and TTL-based API responses survive restarts without keeping everything in RAM
- Immutable historical responses are snapshotted permanently on first successful load, including ended game data, historical standings/leaders, historical shot charts, and past-season team schedules
- Live score polling is demand-driven now: the backend only polls continuously while an SSE client is connected, instead of running a permanent background poll loop
- Redis remains available as an opt-in cache backend if you want it
- The frontend uses
/apivia Vite proxy in development so it does not need to call upstream APIs directly - Some upstream endpoints on balldontlie are not available on the free tier, so the backend includes fallback behavior instead of failing hard