API Schema & Data Models
Complete API schema documentation with GraphQL-style relationship diagrams, data model visualizations, and type definitions.
ποΈ Core Data Models
π GraphQL-Style Schema Relationships
League β Teams β Players Graph
Relationships Explained:
- Solid Lines - Primary relationships (owns/contains)
- Dashed Lines - Historical relationships (traded, played for)
- Colors - Entity types (Red=League, Blue=Team, Green=Player, Purple=Stats)
π Type Definitions
Backend Models (Python/Pydantic)
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime
from enum import Enum
class LeagueTier(int, Enum):
TIER_1 = 1 # Premier leagues
TIER_2 = 2 # Major leagues
TIER_3 = 3 # Regional leagues
TIER_4 = 4 # Local leagues
TIER_5 = 5 # Emerging leagues
class LeagueStatus(str, Enum):
ACTIVE = "active"
PENDING = "pending"
INACTIVE = "inactive"
ARCHIVED = "archived"
class League(BaseModel):
"""
League entity representing a sports organization.
Stored in: Supabase (metadata), Neo4j (relationships)
"""
id: str = Field(..., description="Unique identifier")
name: str = Field(..., min_length=3, max_length=100)
sport: str = Field(..., description="Sport type (basketball, football, etc.)")
tier: LeagueTier = Field(..., description="League classification tier (1-5)")
status: LeagueStatus = Field(default=LeagueStatus.PENDING)
# Metadata
founded: Optional[int] = Field(None, description="Year founded")
headquarters: Optional[str] = Field(None)
website: Optional[str] = Field(None)
# Relationships (populated from Neo4j)
team_count: int = Field(default=0)
player_count: int = Field(default=0)
# Timestamps
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
class Config:
use_enum_values = True
json_schema_extra = {
"example": {
"id": "league_abc123",
"name": "Elite Basketball Association",
"sport": "basketball",
"tier": 1,
"status": "active",
"founded": 2020,
"team_count": 12
}
}π Data Flow Through Models
π― API Endpoint Schema
π Authentication & Authorization Flow
π Data Storage Strategy
Multi-Database Architecture
Storage Decision Matrix
| Data Type | Primary Store | Reason | Backup/Sync |
|---|---|---|---|
| League Metadata | Supabase | Structured, indexed | Neo4j (graph) |
| Team Info | Supabase | Relational queries | Neo4j |
| Player Profiles | Supabase | Complex queries | Neo4j |
| Relationships | Neo4j | Graph traversals | Supabase (denormalized) |
| Career Paths | Neo4j | Historical graph | N/A |
| Live Scores | Firebase | Real-time sync | Supabase (final) |
| User Sessions | Firebase | Auth + real-time | N/A |
| Documents | ChromaDB | Semantic search | Cloud Storage |
π Query Patterns
Neo4j Cypher Query Examples
Find all players who played for multiple teams:
MATCH (l:League {id: $leagueId})-[:HAS_TEAM]->(t:Team)
MATCH (t)-[:HAS_PLAYER]->(p:Player)
WITH p, count(DISTINCT t) AS team_count, collect(DISTINCT t.name) AS teams
WHERE team_count > 1
RETURN p.name, team_count, teams
ORDER BY team_count DESC
LIMIT 20Visual representation:
Supabase SQL Query Examples
Get league standings:
SELECT
t.id,
t.name,
COUNT(CASE WHEN g.winner_id = t.id THEN 1 END) as wins,
COUNT(CASE WHEN g.winner_id != t.id THEN 1 END) as losses,
SUM(CASE WHEN g.winner_id = t.id THEN 3 ELSE 0 END) as points
FROM teams t
LEFT JOIN games g ON (g.home_team_id = t.id OR g.away_team_id = t.id)
WHERE t.league_id = $1
AND g.status = 'completed'
GROUP BY t.id, t.name
ORDER BY points DESC, wins DESC
LIMIT 10;Result structure:
π‘ API Response Schemas
Standard Response Envelope
Success Response Example:
{
"data": {
"id": "league_abc123",
"name": "Elite Basketball Association",
"tier": 1,
"sport": "basketball",
"teams": 12,
"created_at": "2025-11-11T10:00:00Z"
},
"metadata": {
"timestamp": "2025-11-11T10:30:00Z",
"version": "1.0.0",
"request_id": "req_xyz789"
}
}Error Response Example:
{
"error": "validation_error",
"message": "League name must be at least 3 characters",
"details": {
"field": "name",
"provided": "AB",
"minimum": 3
},
"docs": "https://docs.altsportsleagues.ai/api/reference#create-league"
}π State Management Flow
Frontend State Architecture
Code Implementation:
// Zustand Store (Global State)
import { create } from 'zustand';
export const useLeagueStore = create<LeagueStore>((set) => ({
selectedLeague: null,
filters: { sport: 'all', tier: 'all' },
setLeague: (league) => set({ selectedLeague: league }),
updateFilters: (filters) => set((state) => ({
filters: { ...state.filters, ...filters }
})),
}));
// React Query (Server State)
import { useQuery, useMutation } from '@tanstack/react-query';
export function useLeagues() {
return useQuery({
queryKey: ['leagues'],
queryFn: () => fetch('/api/v1/leagues').then(r => r.json()),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
export function useCreateLeague() {
return useMutation({
mutationFn: (data) =>
fetch('/api/v1/leagues', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
}).then(r => r.json()),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['leagues'] });
}
});
}
// Firebase Real-Time (Live Updates)
import { onSnapshot, collection } from 'firebase/firestore';
useEffect(() => {
const unsubscribe = onSnapshot(
collection(db, 'live_games'),
(snapshot) => {
const games = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
updateLiveGames(games);
}
);
return () => unsubscribe();
}, []);π¨ Frontend Type Definitions
TypeScript Interfaces (Auto-Generated from Backend)
// Generated from Pydantic models via sync-types script
export interface League {
id: string;
name: string;
sport: string;
tier: 1 | 2 | 3 | 4 | 5;
status: 'active' | 'pending' | 'inactive' | 'archived';
founded?: number;
headquarters?: string;
website?: string;
team_count: number;
player_count: number;
created_at: string;
updated_at: string;
}
export interface Team {
id: string;
league_id: string;
name: string;
city?: string;
founded?: number;
colors?: string[];
logo_url?: string;
wins: number;
losses: number;
created_at: string;
}
export interface Player {
id: string;
name: string;
birth_date?: string;
position: string;
current_team_id?: string;
jersey_number?: number;
attributes: Record<string, number>;
total_games: number;
career_points: number;
}
// API Response types
export interface LeagueListResponse {
leagues: League[];
total: number;
page: number;
per_page: number;
pages: number;
}
export interface ApiError {
error: string;
message: string;
details?: Record<string, any>;
docs?: string;
}Type Generation Flow
Run type sync:
cd clients/frontend
npm run sync-types
# Reads from: ../../data_layer/schemas/
# Generates to: types/generated/π Data Consistency Patterns
Event-Driven Consistency
Implementation:
from fastapi import BackgroundTasks
@app.post("/v1/leagues", response_model=League)
async def create_league(
league: LeagueCreate,
background_tasks: BackgroundTasks
):
# Primary write (Supabase)
league_data = await supabase.create_league(league)
# Background sync (eventual consistency)
background_tasks.add_task(sync_to_neo4j, league_data)
background_tasks.add_task(sync_to_firebase, league_data)
background_tasks.add_task(invalidate_cache, 'leagues')
background_tasks.add_task(trigger_webhook, 'league.created', league_data)
return league_dataπ Related Schema Documentation
- β Complete Schema Registry - 150+ data schemas
- β API Reference - Interactive API documentation
- β Data Layer - Shared data utilities
- β Data Flow - How data moves through system