Skip to content

DoCurious — Architecture Cheat Sheet

One-pager for engineers joining the project.

Tech Stack

LayerTechnologyVersion
FrameworkReact19
LanguageTypeScript5.9
BuildVite7
StylingTailwind CSS 4@tailwindcss/vite
UI Primitivesshadcn-style src/components/ui/Button, Card, Dialog, etc.
StateZustand5
RoutingReact Router7
FormsReact Hook Form7
AnimationFramer Motion12
i18nreact-i18next14
TestingVitest + Testing Library4
BackendExpress + TypeScript5
ORMPrisma6
DatabasePostgreSQL17
AuthJWT (access + refresh tokens)Custom

How Data Flows

PostgreSQL
    |
    v
Prisma (server/prisma/schema.prisma)
    |
    v
Service Layer (server/src/services/*.ts)
    |  Business logic, validation, AppError
    v
Controller Layer (server/src/controllers/*.ts)
    |  HTTP contract, req/res handling
    v
Route Layer (server/src/routes/*.ts)
    |  Express routes, middleware, Zod validation
    v
────── HTTP / JSON ──────
    |
    v
Real API Layer (src/api/*.real.api.ts)
    |  fetch() calls to /api/portal/...
    v
Adapter Layer (src/adapters/*.ts)
    |  UUID→id promotion, status mapping, field renaming
    v
Zustand Store (src/store/use*Store.ts)
    |  State management, selectors, actions
    v
React Components (src/pages/ + src/components/)

Mock vs Real

Every domain has TWO API files:

  • src/api/challenge.api.tsMock (in-memory, instant, used in dev)
  • src/api/challenge.real.api.tsReal (calls Express backend)

Stores can switch between them. The mock layer is powered by mockDb.ts + seed.ts (3,008 lines of seed data).


Project Structure

src/
├── adapters/          # SQL→FE type mapping (idAdapter, statusMaps, entity adapters)
├── api/               # Mock APIs (*.api.ts) + Real APIs (*.real.api.ts)
├── components/
│   ├── ui/            # shadcn-style primitives (Button, Card, Dialog, Input...)
│   ├── challenge/     # ChallengeCard, ChallengeGrid, ChallengeDiscussion
│   ├── community/     # CommunityCard, FeedPost, MemberList, GoalCard
│   ├── explore/       # DealersChoice, HorizontalRow, FilterPanel, CalendarView
│   ├── filters/       # FilterSidebar, FilterModal, AppliedFilters
│   ├── gamification/  # BadgeCard, LevelProgress, StreakDisplay, Leaderboard
│   ├── gift/          # GiftCard, GiftModal
│   ├── layout/        # AppLayout, Header, Sidebar, AdminNav
│   ├── notifications/ # NotificationCenter, grouped rendering
│   ├── search/        # SearchFilterBar, LocationFilter
│   ├── trackRecord/   # EntryCard, MediaUpload, TrackRecordView
│   ├── common/        # ErrorBoundary, EmptyState, CookieConsent
│   └── debug/         # Debug Panel (Ctrl+Shift+D), dev-only
├── hooks/             # Custom hooks (useFeatureFlag, etc.)
├── i18n/              # react-i18next config + en/es/fr locales
├── lib/               # Pure utility functions
├── pages/
│   ├── auth/          # Login, Register, ForgotPassword, Onboarding (10 pages)
│   ├── public/        # Legal pages: ToS, Privacy, DPA, DMCA, Cookie (9 pages)
│   ├── challenges/    # ChallengeDetail, MyChallenges, TrackRecord (6 pages)
│   ├── explore/       # SavedList, CategoryView, DealersChoice, MapView (4 pages)
│   ├── communities/   # Communities, CommunityDetail, Goals (6 pages)
│   ├── school/        # SchoolDashboard + 20 school admin pages
│   ├── admin/         # 65+ admin toolkit pages
│   ├── vendor/        # VendorDashboard + 10 vendor pages
│   ├── parent/        # ParentDashboard
│   ├── profile/       # Profile, JourneyMap, Badges, XPHistory, Reflections
│   ├── portfolio/     # Portfolios, PortfolioView
│   ├── account/       # Deletion, DataExport, Appeal, LinkedAccounts
│   └── *.tsx          # Dashboard, Explore, Account, Notifications (top-level)
├── routes/
│   ├── index.tsx      # Router definition (150+ routes, lazy loaded)
│   └── guards/        # AuthGuard, ContextGuard, TierGuard, ParentGuard, AgeGuard
├── store/             # 24 Zustand stores
├── theme/             # CSS variable theme system, ThemeProvider
└── types/             # 27 TypeScript type definition files

server/
├── prisma/
│   └── schema.prisma  # 60+ models (40 legacy + 20 modern)
├── src/
│   ├── controllers/   # 19 controllers
│   ├── routes/        # 23 route files
│   ├── services/      # 20 service files
│   ├── middleware/     # auth, requireRole, validate, errorHandler
│   ├── utils/         # jwt, hash, email stubs
│   └── __tests__/     # 19 test files (vitest + supertest)
└── package.json

Key Patterns

1. Zustand Stores

typescript
// Stores follow this pattern:
export const useChallengeStore = create<ChallengeState>((set, get) => ({
  challenges: [],
  isLoading: false,
  fetchChallenges: async () => { /* calls API, sets state */ },
}))
// Access in components:
const { challenges, fetchChallenges } = useChallengeStore()

2. Adapter Layer

typescript
// Adapters convert SQL snake_case → FE camelCase, promote uuid→id
import { adaptChallenge } from '../adapters/challengeAdapter'
const feChallenge = adaptChallenge(sqlRow) // uuid→id, status mapping, etc.

3. Route Guards

typescript
// Nested guard pattern in router:
<AuthGuard>           // Must be logged in
  <ContextGuard       // Must have correct context (school/business/platform)
    context="school"
    roles={['school_admin', 'teacher']}
  >
    <SchoolDashboard />
  </ContextGuard>
</AuthGuard>

4. Backend Middleware Stack

typescript
// Routes use this pattern:
router.post('/:id/classes',
  requireAuth,                    // JWT check
  requireRole('school_admin'),    // Role check
  validate(createClassSchema),    // Zod validation
  schoolCtrl.createClass          // Handler
)

User Roles

RoleContextAccess
General UserpersonalExplore, challenges, communities, gamification
Student Tier 1schoolSchool-only, no private communities/gifts
Student Tier 2school + personalParent-linked, full access
ParentpersonalView child activity, consent controls
TeacherschoolClass management, assignments, reviews
School AdminschoolFull school management, roster, settings
VendorbusinessChallenge authoring, analytics, events
Platform AdminplatformEverything + 65 admin tools

Database: Dual Schema

Legacy models (40+): Direct from MySQL dump. BigInt IDs, snake_case. Used by prodData.service.ts for read-only access to real production data.

Modern models (20+): UUID-based, camelCase. AppUser, AppChallenge, UserChallenge, TrackRecord, XPEvent, Badge, etc. Used by all active services.

Status mapping: ACTIVE→approved, INACTIVE→draft, PENDING→pending_review, archived (new status).


Dev Tools

ToolHow to Access
Debug PanelCtrl+Shift+D — 6 tabs: state, network, flags, theme, routes, logs
Storybooknpm run storybook — 55 component stories
VitePress Docsnpm run docs:dev — knowledge base + dev guide
TypeDoc APInpm run docs:build — auto-generated from source
Testsnpm test (FE) / cd server && npm test (BE)

DoCurious Platform Documentation