personal nba stats page. made for iphone webapp
  • TypeScript 61.2%
  • Python 37.6%
  • CSS 1%
  • HTML 0.1%
  • JavaScript 0.1%
Find a file
eko 9a9df941ff
Some checks failed
CI / Secret scan (push) Has been cancelled
CI / Backend lint, audit & tests (push) Has been cancelled
CI / Frontend lint & build (push) Has been cancelled
Add Highlightly and MySportsFeeds integrations, fix nba_api proxy support, add highlights router
2026-05-20 04:43:25 -04:00
.forgejo/workflows Fix review findings in CI and rate limiting 2026-04-15 20:28:16 -04:00
backend Add Highlightly and MySportsFeeds integrations, fix nba_api proxy support, add highlights router 2026-05-20 04:43:25 -04:00
frontend Add full site smoke test coverage 2026-04-20 22:57:04 -04:00
.env.example Optimize caching and reduce runtime overhead 2026-04-20 21:25:29 -04:00
.gitignore Add automated playoffs smoke test 2026-04-20 21:51:01 -04:00
.gitleaks.toml Add gitleaks secret scanning with pre-push hook 2026-04-15 20:13:10 -04:00
docker-compose.yml Add Redis maxmemory cap and LRU eviction policy 2026-04-15 20:17:25 -04:00
LICENSE Add GPL-3.0-only license 2026-04-13 21:38:04 -04:00
plan.md Add RELEASE.md release checklist 2026-04-15 20:18:57 -04:00
README.md Optimize caching and reduce runtime overhead 2026-04-20 21:25:29 -04:00
RELEASE.md Add RELEASE.md release checklist 2026-04-15 20:18:57 -04:00
SECURITY.md Add SECURITY.md 2026-04-15 20:09:24 -04:00

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_api
  • httpx
  • 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.py mounts frontend/dist when it exists and falls back to index.html for 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 /api via 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