Architecture Overview
Two independent applications
| App | Stack |
|---|---|
Frontend (fe-create-lab-studio) | React 18, TypeScript, Vite, Tailwind CSS, react-i18next |
Backend (be-create-lab-studio) | Node.js, Express, TypeScript, PostgreSQL (pg) |
They communicate over HTTP — the frontend calls the backend REST API using the apiFetch utility, which attaches the Bearer token from the auth service to every request.
Backend — Clean Layered Architecture
src/
domain/ ← Models, DTOs, interfaces (no framework deps)
application/ ← Use-case services
infrastructure/ ← Repositories (raw SQL via pg), external services
presentation/ ← Express controllers and routes
config/ ← App configuration (database, env vars)
cli/ ← CLI entry points (migrate, seed, reset, …)
Data flow:
Route → Controller → Service (application) → Repository (infrastructure) → PostgreSQL
Repositories use raw parameterized SQL — no ORM. Models are plain TypeScript classes.
Frontend — Feature-based structure
src/
components/ ← UI components, organized by feature
contexts/ ← React contexts (auth, scene blocks, episode, etc.)
services/api/ ← One folder per domain (scenes, episodes, tasks, …)
hooks/ ← Shared hooks
constants/ ← permissions.ts, shared enums
i18n/ ← Translation files (en / uk / es)
utils/ ← apiFetch, format, translation helpers
Authentication
All API requests require a JWT Bearer token. The frontend stores the access token in memory via AuthService and refreshes it using a rotating refresh token stored in an httpOnly cookie.
See How Auth Works for the full flow.
Database
PostgreSQL with versioned migrations (numbered SQL files in infrastructure/database/migrations/). Run with:
npm run db:migrate # apply migrations
npm run db:seed # load seed data (roles, permissions, etc.)