🏗️ Architecture Review: World Cup 2026 Tracking Application
Author: Distinguished Engineer
1. System Architecture Diagram
The application utilizes a Zero Trust Service-to-Service Architecture to isolate sensitive backend API integrations (such as API-Sports and YouTube) from direct public access.
graph TD
User([User Client Browser])
subgraph "Public Network"
User -->|1. HTTPS Request| FE[Firebase Hosting Edge]
end
subgraph "Google Cloud Platform (VPC)"
FE -->|2. SSR Execution| Next[Next.js App Server]
Next -->|3. Fetch OIDC Token| Metadata[Compute Metadata Server]
Next -->|4. HTTP + Bearer Token| Proxy[OIDC Identity-Aware Proxy]
subgraph "Internal Services (allUsers IAM revoked)"
Proxy -->|5. Authorized request forwarding| Flask[Python Flask Functions / Cloud Run]
end
end
subgraph "External Third-Party APIs"
Flask -->|6. x-apisports-key| APISports[API-Sports Gateway]
Flask -->|7. developer-key| YouTube[YouTube Data API v3]
end
2. Component Deep Dive
2.1 Frontend: Next.js 16 (React 19)
The frontend utilizes the Next.js App Router and is split into:
* React Server Components (RSC): DailySchedule, MatchResults, HighlightsCarousel, and GroupStandings are executed on the server. They leverage fetchSecureApi to obtain data, which keeps the OIDC proxy token resolution and backend API keys completely hidden from the browser.
* Client Components: Components requiring client-side reactivity (such as ThemeProvider for local storage settings, Header for theme toggles, and TeamFollower for form handling) are declared with "use client".
2.2 Secure API Proxy
The OIDC helper (src/utils/api.ts) automatically detects the execution environment. In production, it utilizes the GCP compute environment's service account to fetch a signed OIDC ID token targeting the backend Flask functions service. In local development (development mode), OIDC authentication is bypassed to support rapid local testing using the Firebase emulator or local Flask.
2.3 Backend Gateway: Firebase Cloud Functions (Python/Flask)
The backend is built as a Flask microservice deployed on Cloud Run via Firebase Functions (Gen 2). To satisfy Zero Trust requirements, the allUsers IAM invoker role is revoked. Only identities with the roles/run.invoker IAM role (which is granted specifically to the Next.js server's identity) are authorized to call the endpoints.
3. Detailed Data Flows
3.1 Server-Side Rendering Path (SSR)
- User requests page
/. - Next.js Server executes the root
page.tsxand parallel async components:DailySchedulefetches/scheduleMatchResultsfetches/resultsGroupStandingsfetches/standingsHighlightsCarouselfetches/highlights
- For each request, the server fetches an OIDC token and sends a
GETrequest to the Cloud Run function. - Next.js compiles the HTML stream and returns a fully rendered page to the user.
3.2 Client-Side Interactive Path (Team Follower API Proxy)
sequenceDiagram
autonumber
actor User as Client Browser
participant NextRoute as Next.js Route Handler (/api/team/[id])
participant NextAuth as Next.js OIDC Proxy
participant Flask as Cloud Run (Flask)
participant External as API-Sports
User->>NextRoute: GET /api/team/USA
NextRoute->>NextAuth: request OIDC token
NextAuth-->>NextRoute: return OIDC token
NextRoute->>Flask: GET /team/USA with Bearer Token
Flask->>External: GET /teams?name=USA
External-->>Flask: return Team ID 3426
Flask->>External: GET /fixtures?team=3426&season=2022
External-->>Flask: return Fixture details
Flask-->>NextRoute: return JSON payload
NextRoute-->>User: return JSON payload
4. Security Architecture & Threat Modeling
| Threat | Risk | Current Mitigation | Proposed / New Mitigation |
|---|---|---|---|
| API Scrape Attack | High | Zero Trust configuration; direct backend calls rejected. | Restrict CORS in Flask to the production frontend domain only. |
| Quota Exhaustion | High | None. Each load calls upstream APIs. | Implement cachetools in Flask to cache upstream API payloads. |
| Token Theft | Medium | OIDC tokens are short-lived and resolved on server-side. | No action required (tokens never touch user browsers). |
| Input Injection | Medium | None. /team/<team_id> passed directly to lookup. |
Implement strict regex validation for the team_id route parameter. |
5. Reliability & Scalability Analysis
5.1 The Caching Gap
Currently, the application operates with no caching. Every page reload triggers 4 upstream API calls. Under peak match-day loads, this model introduces two fatal vulnerabilities: 1. Upstream Rate Limits: API-Sports and YouTube quotas will be exhausted rapidly. 2. RSC Blocking Latency: Next.js Server Components wait for the slowest backend call to complete before returning HTML, yielding high time-to-first-byte (TTFB).
Remediation: Introduce a server-side TTLCache in the Flask backend:
* /schedule: 60s TTL
* /results & /standings: 300s TTL
* /highlights: 600s TTL
5.2 Error Isolation
Currently, the application handles errors by providing hardcoded fallback data. This hides API errors but makes debugging in production difficult.
Remediation:
1. Standardize Flask error payloads ({"error": "description"}).
2. Implement React Error Boundaries around each server component block to ensure one API failure does not crash the entire application dashboard.
6. Architecture Decision Records (ADRs)
ADR 001: Zero Trust Service-to-Service Authorization
- Status: Approved
- Context: The application interfaces with paid external API endpoints (API-Sports). Exposing these credentials or allowing unauthenticated backend endpoints risks financial exposure and quota theft.
- Decision: Configure the backend Cloud Run service to deny
allUsersrequests. Frontend server components must obtain and pass an OIDC ID token, signed by Google and validated at the API Gateway level. - Consequences: Bypasses traditional API-keys/sessions. Requires local development to route through a developer mode (bypassing OIDC) or mock the authenticating proxy.
ADR 002: Implementation of Server-Side Response Caching
- Status: Proposed (Approved for Revamp)
- Context: The current frontend forces a downstream fetch to the API key owner for every client request, creating potential scaling and budget issues.
- Decision: Implement in-memory TTL caching on the Flask backend.
- Consequences: Data will have a slight propagation delay (up to 5 minutes for standings), but infrastructure stability is significantly increased.
7. Known Technical Debt
- Hardcoded Target Audience:
TARGET_AUDIENCEinapi.tsis hardcoded as'https://api-jwiz3cw7wq-uc.a.run.app'. This should be an environment variable. - Hardcoded Season:
CURRENT_SEASONis hardcoded as'2022'infunctions/main.py. This must be externalized to environment variables and updated for 2026. - No Test Safety Net: Zero integration or unit tests for auth wrappers, route proxies, or data mapping functions.