UK Cheerleading Association

The platform that runs a national sport.

Clubs, coaches, cheerleaders, competitions, judging, qualifications, certifications and payments on one data model. From club registration to competition-day paperwork, with no spreadsheets and no paper forms.

Role
Product design · domain architecture · full-stack build · platform migration
Stack
NestJS · Prisma · PostgreSQL · BullMQ · Stripe
Since
2022 · in active development
Status
In production · second generation live
UKCA admin dashboard: events, running order, tickets, card payments, courses, qualifications, tests, certificates, clubs and members in one place
The association's admin: competitions, education and clubs in one place. entries.ukca.org.uk

What it needed to solve

The UK Cheerleading Association runs an entire sport: community clubs and school programmes, coaches and cheerleaders, competitions and judging, coaching qualifications, exams and certifications. For years all of it lived in spreadsheets, email threads and paper forms that didn't talk to each other.

01Clubs registered by hand.

02Every roster change after the deadline meant another round of emails and another version of the spreadsheet.

03The running order was assembled in Excel, and judges were handed score sheets printed from it, in folders.

04Coaching certificates were issued and verified on trust.

05Payments arrived through a patchwork of methods and rarely matched who owed what.

06And records for school cheerleaders, children under 18, needed real protection, not a shared folder.

What was needed was a platform that knows the rules of the sport itself.

What was needed was a platform that knows the rules of the sport itself.

How we built it

The rules of the sport live in the code.

For clubs

01

A dashboard that says what needs doing next.

A club owner signs in and sees what is outstanding: an entry still to be paid, a course coming up, a certificate ready to download.

UKCA club dashboard
02

Every roster checked against the rule book.

The age-division rule sits above the list, each athlete's age on the cut-off date is worked out for them, and a team that cannot compete is flagged long before competition day.

UKCA team assignment with ages at the cut-off date
03

Entering a competition takes six steps.

It ends in a card payment, or an invoice for schools. Every entry keeps its own record of who was on it and what has been paid.

UKCA six-step entry wizard

For the association

04

Two worlds of clubs, one system.

Community clubs and school programmes have different roles and ways of paying, but share one data model. Schools get invoices with a purchase order and Sage reference; community clubs pay by card.

UKCA club directory
05

One ledger for every payment.

Stripe Checkout online, Stripe Terminal at the competition, invoicing for schools. All three land in the same ledger, and every change leaves an audit trail.

UKCA event registrations with payment status
06

Competition day from a single source.

The running order is built by dragging category blocks into place, and start times recalculate with every move. Score sheets per judge, packing lists and envelopes generate from it.

UKCA running order
07

Courses, tests and certificates next to competitions.

Staff see every candidate's result and issue the certificate from the same place, and a public register lets anyone check a coach's qualification.

UKCA online test results

One data model, clear boundaries.

The backend is a NestJS application split by domain: each domain has its own module, controller and service, and the business rules live in the services. Controllers stay thin: validate, call, shape the response.

Data

PostgreSQL through Prisma. 50+ entities hold the whole sport in one schema: club, team, cheerleader, coach, competition, entry, payment, qualification, certificate, ticket. 51 versioned migrations, 1,600 lines of schema.

Job queue

BullMQ on Redis separates slow work from the HTTP request. Sending an email is a job, not a block, with retries and a record of failed jobs.

Storage

Google Cloud Storage with signed URLs. Private documents are read through a time-limited link and never become public.

Access

JWT authentication, roles and policies at route level, and record ownership on top: a club sees its own club, not somebody else's.

Minors' data

Sensitive fields for school cheerleaders are AES-256 encrypted before they reach the database, with a key that never leaves the backend.

Changing the engine mid-flight.

The first generation ran on the Strapi headless CMS. When the scope outgrew it, we built a second generation on NestJS and Prisma and moved onto it domain by domain, with the application running the whole time. The move was completed in September 2026.

dry runChanges nothing

Every domain's migration script runs first without touching data.

--saveWrites a report

A record of exactly what would happen, before it happens.

--applyPerforms the move

Then an audit script compares the old and new state.

A behaviour-parity report and a cutover runbook covered every step, with a way back if it was needed. What moved includes what nobody sees: media from Cloudinary to GCS, certificates, historical payments, entries, users and roles.

Chosen to last.

Runtime
Node.js 20 · TypeScript 5
Framework
NestJS 11
Database
PostgreSQL 16 · Prisma 6
Job queue
BullMQ + Redis 7
Payments
Stripe Checkout, webhooks, Terminal
Documents
pdfkit · ExcelJS · XLSX → PDF · qrcode
Email
React Email · Nodemailer / SMTP
Frontend
React · Vite · MUI · Redux Toolkit · i18next

Security.

  • Roles and ownership checked on every route, not in the interface. The interface hides, the backend forbids.
  • Stripe is never taken at its word: a payment counts only when a signature-checked webhook confirms it.
  • Passwords stored as bcrypt hashes; reset tokens are single-use and time-limited.
  • Secrets come from the environment and a secret manager, never from the repository.

What changed after launch.

The platform runs the sport day to day. Clubs register themselves and enter competitions without back-and-forth. Competition day assembles from the running order, and judges get paperwork that generates itself.

Coaches earn qualifications and walk away with a certificate anyone can check in the register. Schools are invoiced cleanly, community clubs pay by card, and whatever is left is settled with a card reader at the competition, all in the same ledger.

  • Club registration and management
  • Competition entries, community and school
  • Age, division and crossover validation
  • Stripe online, Terminal live, school invoicing
  • Payment ledger with balance and audit trail
  • Running order and competition-day paperwork
  • Online exams, qualifications, certificates
  • Ticketing with QR passes
  • Encryption at rest for minors' data
  • Second-generation platform migration
“TherraCode built the system our sport never had. Clubs, competitions and coaching qualifications finally live in one place - and we trust it.”
UK Cheerleading Association

Have a project like this one?

Tell us what you're building. We'll come back within one business day with a clear plan, and the two people who will build it.

Next project · Platform

The Playbook Studio

A bilingual membership platform that brings health, sport and business strategy together: calendar, content, community and billing in one place.