Application Architecture

Home
Architecture

This project demonstrates a clear split: TypeScript for the product UI and session handling, Go for authoritative booking logic and JWT verification, Supabase for Postgres + Auth.

  • Identity: Supabase Auth issues JWTs; Next keeps the session; Go verifies tokens with OIDC.
  • Data: Single Postgres (Supabase); Go connects with DATABASE_URL (service/pooler role — never expose in the browser).
  • Deploy pattern: Next on Vercel (typical) + Go on Railway (long-lived) + CORS restricted to the web origin.
  • See the Mermaid system diagram on this page (#system-architecture); source lives in apps/web/src/content/system-architecture.mmd.ts.
Architecture
System diagram
How the browser, Next.js, Supabase (Auth + Postgres), and the Go API exchange requests. The Go service never sees the Supabase anon key—only the user's access_token as a Bearer JWT, verified against the Auth issuer (JWKS). Observability in this diagram represents metrics scrape/query flow (Prometheus/Grafana), not centralized logs.

Source: apps/web/src/content/system-architecture.mmd.ts

Deployment
Deployment diagram
How Supabase, Vercel, and Railway services are connected in production.

Source: apps/web/src/content/deployment-architecture.mmd.ts

Go API
HTTP surface
Public vs JWT-protected routes mounted in apps/api/cmd/server/main.go.
MethodPathAuthHandler
GET/healthnoneAPI.Healthapps/api/internal/handlers/handlers.go
GET/api/v1/db-statusBearer (Supabase access_token)API.DBStatusapps/api/internal/handlers/handlers.go
GET/api/v1/availabilityBearer (Supabase access_token)API.Availabilityapps/api/internal/handlers/handlers.go
GET/api/v1/reservationsBearer (Supabase access_token)API.ListMyReservationsapps/api/internal/handlers/handlers.go
POST/api/v1/reservationsBearer (Supabase access_token)API.CreateReservationapps/api/internal/handlers/handlers.go
POST/api/v1/slotsBearer (Supabase access_token)API.CreateSlotapps/api/internal/handlers/handlers.go
POST/api/v1/benchmark/booking-rushBearer (Supabase access_token)API.BenchmarkBookingRushapps/api/internal/handlers/benchmark.go
POST/api/v1/mimic/notification/emailBearer (Supabase access_token)API.MimicEmailPostapps/api/internal/handlers/mimic.go
POST/api/v1/mimic/notification/whatsappBearer (Supabase access_token)API.MimicWhatsAppPostapps/api/internal/handlers/mimic.go
Database invariants
Schema in db/migrations/
  • public.resources — bookable entity.
  • public.slots — UNIQUE (resource_id, starts_at) defines discrete windows.
  • public.reservations — UNIQUE (slot_id) ensures at most one confirmed row per slot (ON CONFLICT target).
  • reservations.user_id REFERENCES auth.users(id) — aligns with JWT sub.
Deploy & env
Operational recap
  • apps/api: Dockerfile; set DATABASE_URL, SUPABASE_URL (or SUPABASE_JWT_ISSUER), CORS_ALLOWED_ORIGINS.
  • apps/web: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, NEXT_PUBLIC_API_URL.
  • See DEPLOY.md at repo root for Supabase redirect URLs and local dev commands.
Deep dive by topic
Expand each section for file pointers, HTTP details, and snippets tied to this repository.

The UI is a Next.js frontend; the Go binary is a stateless HTTP API. Supabase provides Postgres + Auth so identity and booking data stay in one database.

  • apps/web — App Router, Supabase browser client, fetch wrapper with Bearer token.
  • apps/api — chi, CORS, OIDC JWT verification, pgxpool, transactional handlers.
  • db/migrations — DDL (resources, slots, reservations) applied to Supabase Postgres.

Go

  • apps/api/cmd/server/main.goWires chi middleware, CORS, /health, and /api/v1 routes with JWT middleware on protected handlers.

TypeScript / Next

  • apps/web/src/lib/api.tsAttaches Authorization: Bearer for every call to the Go base URL.

This app uses @supabase/supabase-js with the anon key in the browser only. Email/password (and sign-up with email confirmation) talk to Supabase Auth — not to the Go API.

  • signInWithPassword / signUp set a session; cookies are maintained for SSR via @supabase/ssr.
  • Go never receives the anon key. Protected API calls send only the user’s access_token as Bearer.

TypeScript / Next

  • apps/web/src/lib/supabase/client.ts — createClientBrowser Supabase client (anon key).
  • apps/web/src/app/login/page.tsxsignInWithPassword after the user has an account.
  • apps/web/src/app/signup/page.tsxsignUp with emailRedirectTo → /auth/callback?next=…

The UI calls GET /api/v1/availability with the Supabase access token. The handler returns future slots for a resource that have no confirmed reservation (LEFT JOIN + r.id IS NULL).

GET /api/v1/availability?resource_id=<uuid>
  • handlers.Availability validates resource_id, pgxpool.Query with timestamptz scan → RFC3339 strings in JSON.
  • Client: apiFetchJson in apps/web/src/lib/api.ts with Bearer from supabase.auth.getSession().

Go

  • apps/api/internal/handlers/handlers.go — AvailabilitySQL selects open slots; limit 200.

TypeScript / Next

  • apps/web/src/app/book/page.tsxload() reads session, then GET availability for NEXT_PUBLIC_DEFAULT_RESOURCE_ID or demo UUID.