iRace is an invite-only fitness challenge app powered by Strava: set a goal, share a link, and race your crew on real activities. Not affiliated with Strava.
Live: iraceapp.vercel.app
Captures live in docs/strava-submission-screenshots/ — the same set used for Strava API review.
| Landing page | Signed in |
|---|---|
![]() |
![]() |
| Creator consent (step 2) | Join + participant consent |
|---|---|
![]() |
![]() |
| Demo race / leaderboard |
|---|
![]() |
| Connected | Not connected |
|---|---|
![]() |
![]() |
| Privacy Policy | Terms |
|---|---|
![]() |
![]() |
After choosing Connect Strava, Strava handles login and authorization:
| Strava login | Authorize app (scopes) |
|---|---|
![]() |
![]() |
- Strava OAuth with activity-based progress toward distance goals
- Multi-sport challenges (running, cycling, swimming, and more) with per-sport goals
- Shareable invite links, plus a demo challenge for trying the UI without connecting Strava
- Profile dashboard: lifetime stats, activity heatmap, streaks and PBs, head-to-head, challenge history
- Taunts between participants, and optional web push notifications
- English and Spanish, light and dark
- Responsive UI (React, Tailwind, framer-motion)
- Frontend: React 18, TypeScript, Vite, Tailwind CSS, framer-motion, react-router-dom v7
- Backend: Vercel serverless functions (TypeScript) under
api/ - Database: PostgreSQL via Prisma, hosted on Neon
- Auth: Custom JWT access tokens + opaque refresh tokens — no next-auth
- External: Strava OAuth 2.0, REST, and webhooks — all server-side
See docs/ARCHITECTURE.md for how the pieces fit.
Prerequisites: Node.js 20+, npm, and a PostgreSQL instance (local or hosted).
git clone https://github.com/FelixBC/irace
cd irace
npm install # also runs prisma generate
npm run dev # http://localhost:5173Copy .env.example to .env and fill it in. The minimum to boot:
# Server — never exposed to the browser
DATABASE_URL=postgresql://...
DATABASE_URL_UNPOOLED=postgresql://... # same value for local Postgres
AUTH_SESSION_SECRET=a_long_random_string
STRAVA_CLIENT_ID=your_strava_client_id
STRAVA_CLIENT_SECRET=your_strava_client_secret
FRONTEND_URL=http://localhost:5173
# Browser — public, ships in the bundle
VITE_STRAVA_CLIENT_ID=your_strava_client_id
VITE_SUPPORT_EMAIL=you@yourdomain.comAUTH_SESSION_SECRET signs 15-minute access JWTs and hashes 30-day refresh tokens; auth routes error at runtime without it. Never put STRAVA_CLIENT_SECRET behind a VITE_ prefix — that would ship it to every browser.
In Strava API Settings, set Authorization Callback Domain to your hostname only (no scheme, no path). localhost is always allowed for dev.
npm run dev serves the frontend only. For the API routes you also need the Vercel CLI (vercel dev). Full environment reference: docs/DEPLOYMENT.md.
npm run verify # typecheck (both TS projects) + lint + tests
npm run compliance # Strava brand rules
npm run build # prisma generate + production buildCI runs all three on every push and PR to main, but running them locally is faster than waiting. Also scan git status for stray files, and confirm no secret is in the diff — STRAVA_CLIENT_SECRET, AUTH_SESSION_SECRET, DB passwords, VAPID private keys, and webhook tokens belong in env vars only.
| Command | Description |
|---|---|
npm run dev |
Vite dev server |
npm run build |
prisma generate + production build |
npm run preview |
Preview the production build |
npm run verify |
function-count + typecheck + lint + tests — the quality gate |
npm run check:functions |
Guards Vercel's 12-function Hobby ceiling |
npm run typecheck |
TypeScript for both projects (api/+server/+shared/, then src/) |
npm run lint |
ESLint |
npm test / npm run test:watch |
Vitest |
npm run compliance |
Strava brand and trademark checks |
npm run db:setup / npm run db:clean |
Local database helpers |
npm run generate-vapid |
Generate web push VAPID keys |
npm run strava:webhook-register |
Register the Strava webhook subscription |
npm run strava:encrypt-tokens |
One-shot re-encryption of stored Strava tokens |
Optional local Postgres and Redis, defined in docker-compose.yml:
docker compose up -dPoint DATABASE_URL and DATABASE_URL_UNPOOLED at the postgres service to use it.
| Doc | What it covers |
|---|---|
| ARCHITECTURE.md | Runtimes, the Strava token boundary, API surface, auth, data |
| DEPLOYMENT.md | Database, every env var, the canonical domain, rollout hazards |
| STRAVA_COMPLIANCE.md | Strava's rules, reviewer checklist, open risk register |
| STRAVA_SUBMISSION.md | Submission copy, scopes, screenshots, pre-flight |
| STRAVA_WEBHOOK.md | Webhook subscription setup |
| DESIGN_SYSTEM.md | Tokens, components, visual language |
| WEB_PUSH.md | Push notification setup |
| REAL_APP_ENGINEERING_GUIDE.md | What production apps need, mapped onto this codebase |
Contributor and agent conventions live in CLAUDE.md.
- Branch from
main - Make focused changes with clear commit messages
npm run verifymust pass- Open a pull request










