2026 →
Tapapamai
Community sports platform
Challenges, video submissions, verified leaderboards. Full-stack monorepo in production.
4
apps in the monorepo
3
surfaces (API · Admin · Mobile)
71
backend tests (xUnit)
The problem
In a sports community, tracking challenges and performances means using Strava, Instagram or a shared Google Sheet. No single tool lets you publish a challenge with clear rules, receive submissions with video proof, and produce a credible leaderboard. Existing platforms are expensive and designed for affiliated clubs, not informal groups or one-off competitions.
The approach
Monorepo with 4 surfaces: REST API (.NET 8 + PostgreSQL on Railway), Flutter mobile app (iOS/Android/Web), Next.js admin panel and landing page. The athlete submits their score with a video link, the admin validates it before it counts in the leaderboard. Stateless JWT architecture with automatic refresh token on mobile.
Key decisions
WOD statuses calculated client-side
The backend stores startDate/endDate/isActive. OPEN, UPCOMING, CLOSED statuses are computed in the UI, with no cron job to maintain. Single source of truth.
Dockerfile builder forced on Railway
Railpack ignored the Dockerfile ENTRYPOINT, breaking dynamic PORT binding. railway.toml forces builder = "dockerfile" to work around the bug.
Soft-delete on all critical entities
Users, Challenges, Submissions are never physically deleted. IsActive + DeletedAt flags preserve leaderboard history and allow account restoration.
Stack
What I learned
Managing 4 surfaces simultaneously in a monorepo enforces an architectural discipline you wouldn't have with a simple project. The real challenge isn't technical: it's keeping interface contracts in sync across clients without introducing coupling.