How WeDance is built
The system as it exists in the code today. Everything past this page — data model, API, story map — is generated from the code on every build, so it cannot drift.
At a glance
System context
Who uses WeDance and which outside systems it talks to. Solid lines are runtime calls, dotted lines are client-side telemetry.
Rendering diagram…
Diagram source
flowchart LR
dancer([Dancer]) --> web
organiser([Organiser / venue / artist]) --> web
admin([Admin]) --> web
subgraph wd[WeDance 2026 on Vercel]
web[Nuxt app<br/>pages + components] -->|tRPC / REST| api[Nitro server<br/>tRPC routers + API routes]
end
api --> db[(Neon Postgres<br/>70 tables)]
api -->|checkout| stripe[Stripe]
stripe -->|webhook| api
tt[TicketTailor] -->|order webhook| api
api -->|email| resend[Resend]
api -->|places| google[Google Places]
api -->|uploads| cloudinary[Cloudinary]
api -->|reports| linear[Linear]
sync[[sync-v3-events<br/>daily]] -->|read-only| v3[(wedance.vip<br/>v3 Firestore)]
sync --> db
web -.->|analytics| posthog[PostHog]
web -.->|errors + replay| sentry[Sentry]
api -.-> sentryBounded contexts
The domains the data model splits into today. One Nuxt app and one database hold all of them — the boundaries are conventions in the schema and the router files, not separate services.
Dancer accounts, credentials and login sessions, and the apps (MCP assistants) dancers connect via OAuth.
Festival listings, sign-ups and verified tickets, referrals, organiser submissions, and the per-festival social layer (waves, rides, roommates).
Dinner slots at festivals, sign-ups and the generated dinner groups.
Dated public events (v4 archive + live v3 mirror), their flyers and the hosts' saved flyer looks, each dancer's week/year plan, and the unified attendance model.
Venue / artist / organiser profiles under /@handle, canonical cities, follows and the per-city community-group directory.
Bookable spaces, availability and booking requests, plus moderator elections and guideline versions for free community spaces.
City video voting and battles, giveaways (and the ones promoters propose), reviews, "ask locals" recommendations, and promoters' tracked links with what they brought.
The gig board (open roles and offered services, artists' applications) and tonight's hangouts with RSVPs.
Two directories sit next to the relational core rather than in it: profiles are referenced by handle (text) from events, bookings and reviews, and cities are mostly free-text city / city_slug columns that the canonical cities table is meant to replace.
Stack
- Framework
- Nuxt 4 (Vue 3, Vite), SSR on Vercel serverless
- API
- tRPC 11 at /api/trpc (client-side calls) + a few Nitro REST routes for SSR and webhooks
- Data
- Drizzle ORM on Neon Postgres; SQL migrations in server/database/migrations
- Validation
- zod on the server (tRPC inputs), valibot in client forms
- UI
- Tailwind 4 + shadcn-vue (reka-ui) components, the /docs/design system
- Auth
- Own email + password (FirebaseScrypt hashes carried over from v4) and magic links; opaque session tokens in `sessions`, sent as a Bearer header
- Tests
- vitest (unit + router), Playwright + playwright-bdd (e2e, design contract/a11y/visual)
External integrations
Each one verified in the code; the file is where to look first.
| Service | What for | Where |
|---|---|---|
| Neon Postgres | The one production database, via the Neon HTTP driver + Drizzle ORM. | server/utils/db.ts |
| Vercel | Hosting: Nitro `vercel` preset, auto-deploy from main, preview deploys share the prod DB. | nuxt.config.ts |
| Stripe (test mode) | Festival ticket checkout sessions; webhook confirms payment, completes referrals. | server/trpc/routers/festivalSignup.ts · server/api/stripe/webhook.post.ts |
| TicketTailor | HMAC-signed order webhook marks verified ticket holders for the attendee roster. | server/api/webhooks/tickettailor.post.ts |
| wedance.vip (v3 Firestore) | Read-only mirror of upcoming events and their venue/organiser/artist profiles. | scripts/sync-v3-events.ts · server/utils/v3EventSync.ts · server/utils/v3ProfileSync.ts |
| Resend | Transactional email: magic links, password reset, ticket confirmations. | server/utils/email.ts |
| Google Places | City search and resolution into the canonical `cities` table. | server/utils/cities/google.ts |
| Cloudinary | Image uploads (profile photo in settings). | server/api/media/image.post.ts |
| Linear | In-app problem reports and event inquiries become Linear issues. | server/trpc/routers/feedback.ts |
| PostHog | Product analytics (EU), no-op until a key is set. | app/plugins/03.posthog.client.ts |
| Sentry | Error monitoring + session replay, no-op until a DSN is set. | sentry.client.config.ts · sentry.server.config.ts |