Skip to content

site-main · site-shop · site-hypno

The three tenant sites are all TanStack Start apps (server-rendered React with file-based routing). They share the same data-fetching stack and booking integration pattern but differ in scope.

App Port Description Primary data sources
site-main 3000 Main marketing + booking site Directus (content), scheduler public API (booking)
site-shop 3010 Multi-tenant e-commerce storefront Vendure (products + checkout), Directus (content), scheduler API (booking)
site-hypno 3020 Single-tenant booking + storefront reference All three (full reference implementation)

All three sites use:

Package Role
@nexus/business-client High-level customer booking hooks (depends on scheduler-sdk)
@nexus/scheduler-sdk Typed public API client (derived from @nexus/scheduler-contracts)
@nexus/vendure-sdk GraphQL client + React Query hooks for Vendure
@nexus/directus-sdk Directus client + React Query hooks for CMS content
@nexus/ui shadcn + Radix + Tailwind components

Customer booking flows through the contract-first public surface:

@nexus/scheduler-contracts (Zod)
scheduler-api /api/public/:slug/*
@nexus/scheduler-sdk (z.infer types)
@nexus/business-client (hooks: useBook, useAvailability, …)
site-* (React components)

A contract drift between the SDK and server returns a 500 immediately — not a silent failure. See Booking flow.

graph LR
    subgraph "Browser"
        SM[site-main\nor site-shop\nor site-hypno]
    end

    subgraph "Booking"
        SA[scheduler-api\n/api/public/:slug/*]
        SADB[(Scheduler DB)]
    end

    subgraph "Commerce"
        VS[vendure-server\nGraphQL /shop-api]
        VDB[(Vendure DB)]
    end

    subgraph "Content"
        D[directus\nREST + GraphQL]
        DDB[(Directus DB)]
    end

    SM -->|scheduler-sdk\nPOST /api/public/:slug/book| SA
    SA --> SADB
    SM -->|vendure-sdk\nGraphQL queries| VS
    VS --> VDB
    SM -->|directus-sdk\nREST /api/items/*| D
    D --> DDB

TanStack Start handles server-rendering on the first request, then hydrates to a client-side React app. Data is fetched server-side in route loaders using the same SDK hooks as the client — no separate SSR data-fetching layer.

The :slug path parameter scopes all public API calls to a specific business. A single deployment of site-hypno serves one slug; site-shop can serve multiple slugs via routing.