Selected work

Fixture.cc Tournament Platform

A full tournament management platform built with Nuxt 4, Drizzle ORM, and PostgreSQL: format generation for round-robin, elimination, Swiss, and group-knockout competitions, a full admin panel with branding and scheduling, and capability-based access control with encrypted session cookies.

Role
Full Stack
Scope
2026 / Web App / SaaS
Stack
Nuxt 4, Tailwind CSS, Nuxt UI, PostgreSQL, Drizzle ORM, Docker, Traefik
a laptop and phone mockup of the fixture.cc homepage on a dark green backdrop, showing the fixture generator and a public competition page

What It Is

Fixture.cc is a full tournament management platform, not just a one-shot schedule generator. Enter a list of team or player names, pick round-robin, single/double elimination, Swiss, or a group-into-knockout hybrid, and it produces a schedule, a relational data model for tracking results, and a page that stays current as scores come in. From there it covers the rest of running a competition: branding, scheduling across venues, an officials roster, delegated access, and account recovery, covered in detail below.

The full fixture.cc homepage, top to bottom.

Data Model and Format Logic

Every competition is a set of relational records in PostgreSQL: teams, rounds, matches, results. Each format needs different generation logic. Round-robin is a fixed schedule computed once, up front. Single and double elimination need bye handling and seeding so brackets stay balanced with non-power-of-two team counts. Swiss is the hardest of the three: each round’s pairings can only be computed after the previous round’s results are in, based on current standings, so it can’t be precomputed the way round-robin can.

That logic lives entirely server-side, behind Nuxt 4’s Nitro server, with Drizzle ORM as the query layer over PostgreSQL. Drizzle’s schema is TypeScript, so the same competition/round/match types that describe the database also type-check the query results and the API responses built on top of them, rather than the two drifting apart the way they can with raw SQL or a schema-less query builder.

The frontend renders whatever the API returns and holds no bracket state of its own, which keeps the public and admin pages simple regardless of which format generated the data underneath them.

A Full Admin Panel, Not Just a Generator

Past the initial fixture generation, each competition gets a real admin panel: General settings, Branding & sponsors, Teams, Officials, Scheduling, Rebuild fixtures, Scoring & rules, Registrations, Embed & TV, and Sharing & recovery, each its own section rather than one settings page with everything crammed in.

Customization runs deep rather than being a coat of paint: competition and team logos, sponsor logos (with an optional marquee display), a custom accent color, per-league scoring rules (win/draw/loss point values), a roster of named officials with roles (referee, assistant, VAR) assignable per match, and venue/resource scheduling with configurable match duration and buffer time for weekly, daily, or fixed match-window formats. Esports competitions get best-of-N series scoring with per-game results tracked alongside the series score, and a TV mode with its own theme, sponsor visibility, and QR code settings for venue displays. League name, description, and score terminology all support per-locale translations rather than being locked to one language.

The admin dashboard for a live competition, including the recovery-required banner.

Access Control and Security

There’s no account system, but there is a real security layer behind it. Edit access is granted through an encrypted, authenticated session cookie rather than a raw token sitting in a cookie. The one-time management link is only ever checked once to issue that session; it isn’t itself the long-lived credential.

Beyond the single owner link, organizers can generate delegated access links scoped to specific permissions (results entry, scheduling, registrations, or full admin). Only a hash of each link is stored server-side, never the link itself, and each one can be revoked individually or all at once, which is a real capability-based access model, not just “whoever has the URL.”

Sensitive actions, credential checks, and account recovery are backed by hashed credentials, rate limiting, and bot verification, kept deliberately undetailed here since this is a system I actively run in production, not a paper exercise.

Retention

Because most competitions aren’t tied to an account, storage would grow unbounded if nothing ever expired. A new competition is flagged for deletion after a grace period unless the organizer confirms a recovery email, shown as the banner in the screenshot above. That single mechanism is what keeps a free, no-signup product from silently accumulating abandoned data indefinitely.

The public page: the same standings and scoring data, rendered for anyone with the link.

The public page renders from the same underlying data as the admin dashboard, just without the management actions. Scoring trends and a home/away/draw breakdown are computed from the match results already in PostgreSQL, not tracked separately, so the two views can’t disagree with each other.

Stack and Deployment

Frontend is Nuxt 4 with Nuxt UI and Tailwind CSS. The API layer is Nitro, Nuxt’s built-in server, talking to PostgreSQL through Drizzle ORM rather than a separate backend service or a heavier ORM. The whole thing runs in Docker containers behind Traefik, on infrastructure I manage directly.

Fixture.cc’s tournament and fixture management platform has been running in production since 2026. I built it and I still maintain and operate it.