Project Structure
Navigate the apps and @repo/* packages and the rule that apps never import siblings.
A pnpm + Turborepo monorepo: the Nuxt frontend is a thin app under apps/, and all reusable logic lives in @repo/* packages under packages/. Workspace globs (apps/*, packages/*, tooling/*) are declared in pnpm-workspace.yaml; turbo.json orchestrates builds.
The golden rule
Apps never import from sibling apps. Shared logic has exactly one home.
- Shared behaviour (auth, billing, config, DB access, types, helpers) lives in a
@repo/*package. - An app may import from
@repo/*, never reach into another app's source. - Frontend-specific code (components, pages, layouts, composables, plugins) stays inside its app.
apps/
Each frontend is its own app; the backend can run standalone or be hosted by the frontend.
| App | What it is | When it's included |
|---|---|---|
apps/web-nuxt | The Nuxt 4 frontend (Vue 3, @nuxtjs/i18n, Pinia) | Always |
apps/backend | Standalone Hono server (@hono/node-server) that imports @repo/api | Separate-backend deploys; fullstack mode lets the frontend host the API |
apps/docs | The Fumadocs site | generatesaas init --docs; surfaces a "Docs" nav link when config.docs.enabled is true |
generatesaas init ships exactly one frontend: it strips the frontend app you didn't pick, leaving only apps/web-nuxt. Skipping --docs removes apps/docs entirely.
Nuxt 4 keeps source under app/. Per-app nav and banner config live in app/config/; the typed clients live in app/utils/.
apps/web-nuxt/app/
├── pages/ # file-based routes
├── components/ # Vue components
├── layouts/ # page shells
├── composables/ # auto-imported logic
├── middleware/ # route guards
├── plugins/ # Nuxt plugins
├── stores/ # Pinia stores
├── config/ # navbar.ts, sidebar.ts, user-menu.ts, banner.ts
└── utils/ # api.ts (RPC client), auth.ts (auth client)packages/ (@repo/*)
The backend is one Hono app composed from these packages; almost every one reads a flag in @repo/config to decide whether its feature runs.
| Package | Purpose |
|---|---|
@repo/api | The Hono app; mounts internal, public, and Inngest routes and exports AppType for RPC typing |
@repo/auth | Better Auth config (session, social, 2FA, API keys) |
@repo/payments | Stripe/Polar billing, plans, credits, products |
@repo/database | Drizzle schema and client |
@repo/mail / @repo/sms | Transactional email and SMS senders |
@repo/storage | S3/local file uploads |
@repo/notifications / @repo/admin-notifications | In-app user notifications and admin event alerts |
@repo/audit | Audit logging |
@repo/content | Shared markdown (legal pages + blog) under en/ and ro/ |
@repo/ai | AI helpers built on the ai SDK |
@repo/runtime | Env, logger, Redis, rate-limit store, request helpers |
@repo/config | The central config object - the feature-flag source of truth. See Configuration |
@repo/i18n | Shared translations |
@repo/utils | Framework-agnostic helpers |
Typed clients
The Nuxt app builds its own clients against the shared backend - both in apps/web-nuxt/app/utils/. A backend route change is type-checked here automatically.
| Client | File | How |
|---|---|---|
| RPC client | api.ts | hc<AppType> from Hono; AppType flows from @repo/api. See API |
| Auth client | auth.ts | createAuthClient from better-auth/vue |
SSR cookie forwarding is framework-specific - keep it in the app, never abstract it into a package. Nuxt SSR self-requests use $fetch.raw (undici rejects relative URLs from globalThis.fetch); never import("@repo/api") from app/, which triggers a circular renderer init.
Builds
Turborepo runs build, check-types, lint, dev, and test with ^build ordering, so packages compile before the app. pnpm catalogs in pnpm-workspace.yaml pin shared versions (hono, better-auth, drizzle-orm, zod) across the workspace.
Frequently asked questions
Where do I put logic shared between the frontend and backend?
In a @repo/* package. Anything an app imports must come from @repo/*, never from another app.
Why does the Nuxt app define its own RPC and auth clients instead of importing them?
SSR cookie forwarding differs per framework, so each app owns its api.ts and auth.ts. They stay typed because AppType is imported from @repo/api.
When do I need apps/backend?
Only for the separate-backend deployment, where the Hono API runs on its own host. In fullstack mode the Nuxt app hosts the API itself.