Data Flow & Integration
Understanding how data flows through the AltSportsLeagues.ai ecosystem, from user interactions to database storage and back.
π Complete Data Flow
π Flow Example 1: User Browsing Leagues
Step-by-Step Flow
User Visits Page
User navigates to https://altsportsleagues.ai/leagues
Action: Browser sends HTTPS request to Cloudflare
Next: Cloudflare routes to Vercel
Page Renders (SSR/SSG)
Vercel serves the Next.js page (server-side rendered or static)
Action: Next.js App Router renders page
Data Needed: League list from API
Next: Component makes API call
Frontend Calls API
Browser executes: fetch('/api/v1/leagues')
URL Seen by Browser: altsportsleagues.ai/api/v1/leagues
Vercel Rewrite: api.altsportsleagues.ai/v1/leagues
Note: Rewrite is invisible to browser!
Backend Processes Request
Cloud Run receives request at /v1/leagues endpoint
Router: FastAPI router handles /v1/leagues
Validation: Pydantic models validate request
Next: Query databases
Database Queries
Backend queries Neo4j and Supabase in parallel
Neo4j Query: Get league graph relationships
Supabase Query: Get league metadata
Merge: Combine results
Response Returns
JSON response flows back through the chain
Backend: Serializes to JSON
Cloudflare: Proxies response
Vercel: Forwards to browser
Browser: Receives data (same origin, no CORS!)
Frontend Renders
React component updates with league data
React Query: Caches response
Zustand: Updates global state
UI: Displays league list
Total Time: ~300-500ms (end-to-end)
π‘ Flow Example 2: External API Consumer
API Request Example
curl https://api.altsportsleagues.ai/v1/leagues \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"Response:
{
"leagues": [
{
"id": "league_123",
"name": "Sample League",
"tier": 1,
"sport": "basketball"
}
],
"total": 1,
"page": 1
}π€ Flow Example 3: n8n Automation
Webhook Configuration
n8n Webhook URL:
https://n8n.altsportsleagues.ai/webhook/league-intakeTrigger Example:
{
"event": "new_questionnaire",
"email_id": "msg_abc123",
"attachment_count": 1
}π Data Layer Integration
Cross-Service Data Sharing
How It Works
Backend (Python):
from data_layer.schemas.league import LeagueSchema
from data_layer.shared.python import validators
league = LeagueSchema(**data)
validated = validators.validate_league(league)Frontend (TypeScript):
import type { League } from '@/types/league';
import { validateLeague } from '@/lib/validators';
const league: League = await response.json();
const validated = validateLeague(league);Docs Site (Schema Injection):
<!-- In MDX file -->
{{schema:schemas/league/tier_classification.py}}
<!-- Renders as formatted schema documentation -->π Real-Time Data Flow
WebSocket Integration (Future)
Firebase Real-Time Pattern
Frontend subscribes to updates:
import { onSnapshot } from 'firebase/firestore';
// Subscribe to league updates
const unsubscribe = onSnapshot(
leagueRef,
(snapshot) => {
const updatedLeague = snapshot.data();
updateUI(updatedLeague);
}
);Backend publishes updates:
from firebase_admin import firestore
# Update league and trigger real-time sync
league_ref.update({
'status': 'active',
'updated_at': firestore.SERVER_TIMESTAMP
})π¦ Batch Processing Flow
Large Dataset Processing
Use Cases:
- Daily league data updates
- Weekly statistics aggregation
- Monthly report generation
- Bulk league onboarding
π― Request Routing Logic
Intelligent Routing
Path Routing in Backend
FastAPI router configuration:
from fastapi import FastAPI, APIRouter
app = FastAPI()
# Version 1 API Router
v1_router = APIRouter(prefix="/v1")
# Include domain routers
v1_router.include_router(leagues_router)
v1_router.include_router(teams_router)
v1_router.include_router(players_router)
# Mount v1 router
app.include_router(v1_router)
# Health check (no version prefix)
@app.get("/health")
async def health_check():
return {"status": "healthy"}
# Result:
# /v1/leagues β API endpoints
# /v1/teams
# /health β Utility endpoints
# /docs β DocumentationπΎ Database Access Patterns
Neo4j (Graph Queries)
Use Case: Complex relationship traversals
Example Query:
// Find all players who played for multiple teams in a league
MATCH (l:League {id: $leagueId})-[:HAS_TEAM]->(t:Team)
MATCH (t)-[:HAS_PLAYER]->(p:Player)
WITH p, count(DISTINCT t) as team_count
WHERE team_count > 1
RETURN p.name, team_count
ORDER BY team_count DESCBackend Code:
from data_layer.shared.python.neo4j_utils import run_query
async def get_multi_team_players(league_id: str):
query = """
MATCH (l:League {id: $leagueId})-[:HAS_TEAM]->(t:Team)
MATCH (t)-[:HAS_PLAYER]->(p:Player)
WITH p, count(DISTINCT t) as team_count
WHERE team_count > 1
RETURN p.name, team_count
ORDER BY team_count DESC
"""
results = await run_query(query, {"leagueId": league_id})
return resultsSupabase (Relational Queries)
Use Case: Structured data retrieval
from data_layer.shared.python.supabase_client import get_client
async def get_league_schedules(league_id: str):
supabase = get_client()
response = await supabase.table('schedules') \
.select('*') \
.eq('league_id', league_id) \
.order('game_date', desc=False) \
.execute()
return response.dataFirebase (Auth & Real-Time)
Use Case: User authentication and live updates
// Frontend: Subscribe to real-time updates
import { onAuthStateChanged } from 'firebase/auth';
import { onSnapshot } from 'firebase/firestore';
// Auth listener
onAuthStateChanged(auth, (user) => {
if (user) {
// User signed in
subscribeToLeagues(user.uid);
}
});
// Real-time data listener
const unsubscribe = onSnapshot(
collection(db, 'leagues'),
(snapshot) => {
const leagues = snapshot.docs.map(doc => doc.data());
updateUI(leagues);
}
);π Data Synchronization
Cross-Database Consistency
Implementation:
from contextlib import asynccontextmanager
@asynccontextmanager
async def transaction_handler():
"""Ensure consistency across databases"""
neo4j_tx = await neo4j.begin_transaction()
supabase_tx = await supabase.begin()
try:
yield (neo4j_tx, supabase_tx)
# Commit all or none
await neo4j_tx.commit()
await supabase_tx.commit()
# Trigger Firebase real-time update
await firebase.notify_update()
except Exception as e:
await neo4j_tx.rollback()
await supabase_tx.rollback()
raise eπ¨ Frontend State Management
Zustand + React Query Pattern
Code Example:
// Zustand store
import { create } from 'zustand';
export const useLeagueStore = create((set) => ({
selectedLeague: null,
setLeague: (league) => set({ selectedLeague: league })
}));
// React Query hook
import { useQuery } 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
});
}
// Component usage
function LeaguesList() {
const { data: leagues } = useLeagues();
const { setLeague } = useLeagueStore();
return (
<div>
{leagues?.map(league => (
<div key={league.id} onClick={() => setLeague(league)}>
{league.name}
</div>
))}
</div>
);
}π Performance Optimization
Data Flow Optimizations:
- Parallel database queries (Neo4j + Supabase)
- React Query caching (reduce API calls)
- Vercel edge caching (static content)
- Redis caching (backend, optional)
- Firebase local persistence
Caching Strategy
π Related Architecture Pages