Skip to main content

System Architecture

┌─────────────────────────────────────────────────────────┐
│                 MOBILE APP (Expo/React Native)            │
│  ┌───────────────────────────────────────────────────┐   │
│  │  Screens → Components → Hooks → Context/State     │   │
│  │  supabase.rpc() / supabase.from() (direct calls)  │   │
│  └───────────────────────────────────────────────────┘   │
└──────────────────────────┬──────────────────────────────┘
                           │ Direct HTTPS (anon key + RLS)

┌─────────────────────────────────────────────────────────┐
│                   BACKEND (Supabase)                      │
│  ┌──────────┐  ┌──────────────┐  ┌──────────────────┐   │
│  │   Auth   │  │   Database   │  │     Storage       │   │
│  │ (Users)  │  │ (PostgreSQL) │  │ (Images/Files)    │   │
│  └──────────┘  └──────────────┘  └──────────────────┘   │
│  ┌───────────────────────────────────────────────────┐   │
│  │         Row Level Security (RLS) Policies          │   │
│  └───────────────────────────────────────────────────┘   │
└──────────────────────────┬──────────────────────────────┘


┌─────────────────────────────────────────────────────────┐
│  Loops.so (Email Notifications & Campaigns)              │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│              WEB INTERIM (Vercel — apps/web-interim)      │
│  Auth callbacks, deep link verification, landing page     │
│  Separate Next.js app — no content CRUD API routes        │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│              WEB CLIENT (Phase 2 — apps/web)              │
│  Scaffolded Next.js app with API routes + component lib   │
│  Will become the full web client when Phase 2 begins      │
└─────────────────────────────────────────────────────────┘

Separation of Concerns

The codebase follows a strict layered architecture:
LayerResponsibilityLocation
UIReact components, presentation onlycomponents/
LogicBusiness logic via custom hookshooks/
DataAPI calls and data fetchinglib/api/
StateGlobal state via Context providerscontext/

Design Principles

The mobile app calls Supabase directly via supabase.rpc(), supabase.from(), and supabase.auth for all reads, writes, and authentication. There are no web API route dependencies — everything goes through the Supabase client SDK with RLS policies as the primary security layer.The web client (Phase 2) will route all data mutations through API routes (/api/*) with the Supabase service key used only server-side. RLS policies act as the last line of defense.
UI components live in componentlibrary/ (shadcn/ui). Feature components are organized by domain: auth/, recipe/, user/. Check existing components before creating new ones.
Strict TypeScript throughout — no any types. Database types use snake_case, API responses use camelCase. Conversion utilities handle the mapping.
The @recipe-room/shared package provides types, Zod schemas, constants, and utilities used by both web and mobile apps.

Data Flow

Create Recipe (Mobile)

User Action → Form Validation (Zod) → Upload Images to Storage →
supabase.rpc('create_recipe', {...}) → Response →
React Query Cache Update → Toast Notification → Redirect

Image Upload

Select Image → Client Resize/Compress →
supabase.storage.from('bucket').upload() → Return Public URL →
Update Form State → Display Preview

Follow User (Mobile)

Tap Follow → FollowButton local state update (instant UI) →
supabase.from('follows').insert() → On success: invalidate profile/follower queries →
Trigger updates follower_count + following_count