How tkaardvark/axis works
1. High-level shape
Axis Analytics is a two-tier application: a React frontend in client/ and an Express/PostgreSQL backend at the repo root. The frontend renders NAIA basketball analytics views, while the backend serves JSON APIs, computes/serves derived metrics, and hosts the scheduled data-ingestion jobs.
2. User-facing frontend
The client is a Vite-built React 19 app with React Router. The main page areas are team analytics, players, conferences, tournament bracket projection, and scouting/comparison tools. The app is organized as feature components rather than by technical layer, so each route has its own assembled view and supporting panels/charts.
3. Frontend data boundary
Frontend components pull data through client/src/utils/api.js and related helpers, which form the boundary to the backend. The app also includes formatting/normalization utilities, chart helpers, and stat-group definitions, indicating that raw API data is transformed on the client into table rows, chart series, tooltips, and comparison metrics.
4. State, gating, and personalization
ThemeContext and TeamLinkContext provide cross-app UI state. Authentication-related wrappers such as RequireAuth, AdminOnly, and PostAuthRedirect, plus hooks like useUserTeam and useUserTier, show that some experiences are personalized or restricted, especially scouting/admin surfaces. This creates a clear split between public analytics pages and authenticated staff tooling.
5. Design system
client/src/design-system is a separate internal UI system with tokenized colors, spacing, typography, and elevation, plus reusable primitives like Button, Card, Badge, SearchInput, Select, and Tabs. That subsystem acts as the visual foundation for the app and also contains standalone UI-kit examples and guidelines, so the main product UI is built from a controlled set of presentation primitives.
6. Backend API and domain routes
The server side is centered on server.js, with route modules for teams, players, conferences, matchup, tournament, bracketcast, scoutingReports, staffHub, and metadata. These route files represent the API surface area and likely map directly to the major pages in the frontend. db/pool.js is the shared PostgreSQL connection layer.
7. Analytics and domain logic
The repo keeps core business logic in root-level utils/, including adjusted ratings, percentile math, tournament classification, team resolution, conference champions, geometry/quadrant helpers, and dynamic box-score/stat transforms. This means the backend is not just CRUD; it synthesizes advanced basketball metrics and projections from imported game and player data.
8. Data model and migrations
Schema evolution is managed by the migrations/ directory, which adds players, import logs, game flags, box-score stats, analytics fields, location data, excluded-team handling, season metadata, tournament flags, aliases, and player photos over time. The presence of many additive migrations suggests a long-lived analytics schema with computed fields and historical data correctness requirements.
9. Data pipeline
The repository’s most important non-UI subsystem is the ingestion pipeline. Scripts like scrape-team-urls.js, scrape-conferences.js, scrape-logos.js, scrape-player-photos.js, and the import-*.js scripts pull data from Presto Sports, then populate PostgreSQL. After import, analytics scripts and helpers compute derived ratings, standings, and tournament-related outputs. The official NAIA stats provider is therefore an external dependency that feeds the entire product.
10. Scheduled automation
scheduler.js runs the production refresh cycle via node-cron. In production, the app periodically scrapes, imports, and recalculates analytics without manual intervention. That makes the backend both an API service and a data-processing worker, with scheduling tied into the same Node deployment.
11. Configuration and season-specific reference data
The config/ directory contains domain reference data such as excluded teams, conference areas, team locations, exhibition overrides, and tournament bracket definitions. JSON mappings like team-urls-2024-25.json and team-urls-2025-26.json indicate the system is season-aware and relies on cached discovery results for external source endpoints.
12. Deployment and runtime boundaries
The app is designed for Render: a Node.js API service, a static frontend build from client/dist, and managed PostgreSQL. Root and client package.json files reflect the split runtime, with the API handling server-side logic and cron jobs, and the client serving as a separate static SPA.
13. Documentation as architecture support
The docs/ folder is substantial and appears to be part of the system design, not incidental notes. It documents the data pipeline, database schema, API contracts, legal copy, and stakeholder-facing behavior, which is useful for understanding how the backend, database, and frontend are intended to interact.
Main entry points: components Route views · pool.js DB pool · teams.js API routes · computeAdjustedRatings.js Domain logic
Components 16
Client app
React/Vite single-page app for analytics, scouting, and legal pages.
- React app · Main Vite-built React application with routed analytics views.
client/src/App.jsx - Route views · Team, player, conference, matchup, tournament, bracketcast, and scouting pages assembled as feature components.
client/src/components - API client · Helpers that fetch, normalize, and format backend data for tables, charts, and tooltips.
client/src/utils/api.js - State & access · Theme, team-link state, and auth gating for personalized or restricted surfaces.
client/src/contexts/ThemeContext.jsx - Design system · Tokenized UI kit and reusable primitives that underpin the product interface.
client/src/design-system - Client analytics · Client-side stat grouping, percentiles, normalizers, and chart/tooltip helpers.
client/src/utils/statGroups.js
Server & data
Express API, database layer, analytics logic, ingestion jobs, and schema evolution.
- Server entry · Root Node service that mounts routes and serves the API.
server.js - API routes · Domain route modules for teams, players, conferences, matchup, tournament, bracketcast, scouting, staff, and metadata.
routes/teams.js - DB pool · Shared PostgreSQL connection pool used by the API and jobs.
db/pool.js - Domain logic · Basketball calculations for adjusted ratings, tournament classification, team resolution, champions, geometry, and box-score transforms.
utils/computeAdjustedRatings.js - Schema & migrations · Additive migration history for players, games, analytics fields, season metadata, aliases, photos, and tournament flags.
migrations - Ingestion jobs · Import and scrape scripts that pull Presto Sports data and populate the database.
import-players.js - Scrapers · Scripts that discover team URLs, conferences, logos, and player photos from upstream sources.
scrape-team-urls.js
Automation & config
Scheduled refresh, season reference data, and deployment/runtime boundaries.
- Scheduler · Production refresh orchestration that periodically runs scraping, importing, and recalculation tasks.
scheduler.js - Season config · Conference areas, excluded teams, locations, bracket definitions, and cached season URL maps.
config/tournament-bracket-2026.js - Render deploy · Node API service, static frontend build, and managed PostgreSQL deployment boundary.
render.yaml
Connections 21
- React app → Route views: renders · The app composes its main pages from feature-oriented route components.
- Route views → API client: loads data · Screens retrieve analytics and domain data through the client API helpers.
- Route views → State & access: uses · Views depend on shared theme, team-link, and access-control wrappers.
- Route views → Design system: builds with · Product UI is assembled from shared design-system primitives and tokens.
- Route views → Client analytics: transforms · Route components turn raw responses into chart series, tables, and comparisons.
- API client → API routes: calls · Frontend helpers request JSON from the backend route surface.
- Server entry → API routes: mounts · Express server registers the domain route modules.
- API routes → DB pool: queries · Routes read and write through the shared PostgreSQL pool.
- API routes → Domain logic: computes · API responses incorporate derived basketball metrics and projections.
- Domain logic → DB pool: reads/writes · Analytics helpers and refresh flows operate on persisted team, player, and game data.
- Schema & migrations → DB pool: defines · Migrations shape the schema used by the shared database connection.
- Ingestion jobs → DB pool: populate · Import scripts load source data into PostgreSQL tables.
- Scrapers → Ingestion jobs: feed · Scraping scripts discover upstream source URLs and assets for imports.
- Scheduler → Scrapers: triggers · Cron orchestration kicks off periodic discovery and refresh jobs.
- Scheduler → Ingestion jobs: runs · Scheduled automation executes the import and backfill pipeline.
- Scheduler → Domain logic: recalculates · Refresh cycles recompute ratings, standings, and projections after ingestion.
- Season config → Scrapers: guides · Reference data constrains source discovery, team mapping, and bracket logic.
- Season config → Domain logic: parameterizes · Season-specific exclusions and bracket rules shape analytics calculations.
- Render deploy → Server entry: hosts · The Render runtime serves the Node API and scheduled worker.
- Render deploy → React app: serves build · The frontend is deployed as a static SPA build.
- Render deploy → DB pool: connects to · Managed PostgreSQL backs the application data layer.