FrontendHigh-Level Design
High-Level Design
Architecture overview of Falsafa's frontend: how auth, storage, chat, payments, and the API layer fit together.
The frontend is organized into five subsystems that communicate with Supabase, Stripe, and the Python backend. Each subsystem is covered in detail in the pages that follow. This page shows how they connect.
Subsystem Map
What Each Subsystem Owns
| Subsystem | Responsibility | Data store |
|---|---|---|
| Auth | User login, registration, session cookies, role checks, blocked-user enforcement | Supabase Auth + profiles + user_roles tables |
| API Layer | All CRUD operations, backend proxying, file upload orchestration | Supabase DB + Storage, Stripe, Python backend |
| Storage | Book covers, book files, profile images, category images | Supabase Storage buckets |
| Chat | Session creation, message history, SSE streaming, user preferences | Supabase DB (sessions + messages), Python backend (LLM) |
| Payments | Checkout flow, payment intent creation, webhook processing, library assignment | Stripe, Supabase DB (purchases + user_library) |
| Admin | Stats dashboard, content moderation, user management, system settings | Supabase DB (via service-role client) |
Key Design Patterns
- SSR-first, then client. Pages render on the server with Supabase session cookies. Client components add interactivity on top.
- Service-role client for privileged operations.
getAdminClient()creates a Supabase client with the service-role key, bypassing RLS. Used for admin CRUD, book upload, and role checks - anything that the authenticated user's row-level permissions would otherwise restrict. - Debug mode escape hatches. When
NEXT_PUBLIC_DEBUG_MODE=true, every auth check, Stripe call, and Supabase initialization can be bypassed with mock data. All pages remain navigable without external services running. - API routes as the integration layer. Every external service call (Supabase writes, Stripe API, backend proxying) goes through a Next.js API route. Pages never call external services directly. This keeps the trust boundary at the API layer.
- Snake-case at the boundary. Supabase stores data in snake_case columns. The TypeScript types in
types/index.tsuse camelCase. Conversion happens inlib/utils/transform.tsand is applied selectively by each DB query module.