My first multi-tenant product: a league-operations platform built from scratch for youth sports. Registrations, payments, scheduling, team formation, standings, and communications, now opening from a single flag-football league to more than one operator.
The league was stuck on a dying platform (Sports Connect, EOL Fall 2026) and nothing off-the-shelf fit, so I built what should have existed. It started as League-OS running the Advantage Flag League, and the rebrand to Advantage Sports opens that same platform to more than one operator.
Why it existsMost of the platform is boring CRUD done carefully. Two pieces are genuinely interesting: check processing and team formation. Cash, check, and money order payments still dominate youth sports, so the app has to turn a photo of a check into a posted payment. It works as a pipeline that spans Vercel and Google Cloud: the browser requests a signed upload URL and writes the check PDF straight to Google Cloud Storage, then Vercel kicks a containerized Cloud Run service (authenticated with a Google Auth Library ID token) that renders the PDF to PNGs using pdf-to-img and @napi-rs/canvas and writes the images back to GCS. A job engine in Postgres orchestrates the multi-step work. Each job step runs inside a Vercel route and uses after() to self-chain to the next step, which keeps every invocation under the 60 second serverless cap. Lease-based serialization prevents two workers from processing the same job, and a cron recovery job picks up anything stale. Team formation is the other hard part. Sorting hundreds of kids into fair teams used to be a weekend of spreadsheets and hurt feelings, so I built a real solver: a greedy first pass seeds the teams, then simulated annealing refines them against weighted hard rules and soft preferences. It balances grade levels, quarterback counts, and how many kids come from each school, honors teammate-together and do-not-pair requests, keeps coaches near the players they already know, and makes sure every kid recognizes at least one familiar face on their roster. Formation runs as a background job and produces a few proposals an admin can compare side by side, with plain-English narration of the tradeoffs, and nothing goes live until a human accepts one. None of this is generic SaaS plumbing. It only exists because the league actually needed it.
Fig. 01 · UI preview

Spec · 01Rev. 04·26
- Status
- Live · shipping
- Users
- 1,500 families
- Stack
- Next.js · Drizzle
- DB
- Neon Postgres
- Pay
- Stripe
- Host
- Vercel