Ask F1
Natural-language questions over 75 seasons of Formula 1 data
- Questions in plain English
Ask a question in plain English — "Who won the most races at Monza?" — and get a natural-language answer backed by real data, generated on the fly from a 75-season F1 database via text-to-SQL.
- Guarded queries
Kept the LLM's blast radius small with a guarded pipeline: Gemini, via Microsoft.Extensions.AI, produces a single SELECT, a SqlGuard step rejects multi-statement input and any write or DDL keyword, and execution runs as a read-only Postgres role inside a READ ONLY transaction with a 5-second statement timeout and an enforced LIMIT, before Gemini summarizes the rows.
- API & quotas
Built the API in ASP.NET Core on .NET 10 with Clerk JWT authentication and first-sign-in user provisioning, EF Core used to author migrations that ship as reviewed idempotent SQL, and HybridCache answer caching keyed by a version bumped whenever prompts or the dataset change.
Guarded cost and abuse with a per-user daily question quota plus fixed-window rate limiting in front of the LLM, and persisted every question so users can review their history.
- Data pipeline
Wrote a two-stage offline data pipeline: a Python script cleans the raw Kaggle CSVs (null normalization, country and status canonicalization, lap-time parsing with anomaly cross-checks, referential-integrity validation and a generated report), then a .NET CLI bulk-loads them through Npgsql binary COPY in foreign-key-safe order inside one re-runnable transaction — about 700k rows across 14 tables.
- Frontend
Shipped a chat-style React 19 frontend on Vite with Clerk and TanStack Query.
- Deployment
Deployed both apps on Railway with Neon Postgres — the backend as a multi-stage Docker image, the frontend as a static build, both with GitHub Actions CI.