Architecture Overview
Route Groups
The app is organized into four route groups under src/app/:
| Route Group | Purpose |
|---|---|
(app)/ | Main user-facing app — wrapped in session, Google Maps, tRPC, search, and location providers |
admin/ | Admin dashboard — separate layout, role-guarded |
studio/ | Sanity Studio — embedded CMS |
api/ | API routes — tRPC HTTP handler, NextAuth handlers, Sanity draft mode |
Data Layer
Database Schema
Schema files live in src/db/schema/:
auth-schema.ts— users, accounts, sessions (NextAuth tables)core-schema.ts— places, reviews, photos, types, self-reported reviews, promoted/verified placesrelations.ts— Drizzle relation definitionsviews.ts— materialized views (e.g.,placeSummary)
The DB client is a singleton in src/db/index.ts, cached in development to prevent connection exhaustion.
Migrations are generated by drizzle-kit and stored in src/db/migrations/.
tRPC Structure
tRPC provides type-safe API calls between client and server:
- Router definition:
src/lib/api/server/index.ts— merges sub-routers:place,category,user,role,analytics,geolocation - Procedures:
src/lib/api/server/trpc.ts— defines access levels:publicProcedure— no auth requiredprivateProcedure— requires authenticated sessionadminProcedure— requires ADMIN or SUPERADMIN rolesuperadminProcedure— requires SUPERADMIN role
- Client hook:
src/lib/api/client.ts— exportsapi(tRPC React Query hooks) - Provider:
src/lib/api/provider.tsx— wraps the app with tRPC + React Query
Server Actions
Mutations are handled through Next.js server actions, colocated with their pages in _actions.ts files:
src/app/(app)/(auth)/login/_actions.ts— Google sign-in, Resend magic linksrc/app/(app)/(auth)/onboarding/_actions.ts— user onboardingsrc/app/(app)/map/place/[id]/_actions.ts— post review, fetch reviews/photossrc/app/(app)/profile/_actions.ts— profile reviews, delete/update reviewsrc/app/(app)/profile/edit/_actions.ts— update profile, delete accountsrc/app/admin/submit/_actions.ts— self-report venue submission
Authentication
Auth is handled by NextAuth v5 (beta), configured in src/lib/auth.ts:
- Providers: Google OAuth + Resend (magic link email)
- Adapter: Drizzle adapter connecting to PostgreSQL
- Session strategy: JWT with role injection in the JWT callback
Roles
Three roles defined in src/lib/roleUtils.ts:
| Role | Access |
|---|---|
USER | Default role, can leave reviews |
ADMIN | Access to admin dashboard |
SUPERADMIN | Full access including user management |
Guard components live in src/components/auth/ for both client and server-side protection.
Key Conventions
- Server actions live in
_actions.tsfiles colocated with their page - Form schemas (Zod) live in
_form-schema.tsfiles colocated with their page - UI components: Customized shadcn/ui in
src/components/ui/— CSS variables define the theme (primary = dark green, secondary = light yellow). New shadcn imports need manual styling adjustments per the Figma design. - Path alias:
@/maps tosrc/ - SVG imports: Handled as React components via
@svgr/webpack