Content
1# CLAUDE.md — Full-Stack SaaS
2
3## Project overview
4<!-- Brief description. Example: B2B project management tool with Stripe subscriptions. -->
5
6## Monorepo structure
7```
8apps/
9 web/ # Next.js 15 frontend (port 3000)
10 api/ # Express/Hono backend (port 4000)
11packages/
12 db/ # Prisma schema + client (shared)
13 ui/ # Shared component library
14 utils/ # Shared TypeScript utilities
15```
16
17## Tech stack
18| Layer | Technology |
19|---------------|-------------------------------------|
20| Frontend | Next.js 15, TypeScript, Tailwind v4 |
21| Backend | Hono (Node 22), Zod |
22| Database | PostgreSQL, Prisma ORM |
23| Auth | NextAuth v5 + JWT API tokens |
24| Billing | Stripe (Checkout + Webhooks) |
25| Email | Resend |
26| Monorepo | Turborepo + npm workspaces |
27
28## Development commands
29```bash
30npm run dev # start all apps in parallel
31npm run dev --filter=web # start only frontend
32npm run build # build all packages
33npm run test # run all test suites
34npm run db:migrate # run Prisma migrations
35npm run db:seed # seed demo data
36npm run stripe:listen # forward Stripe webhooks locally
37```
38
39## Multi-tenancy model
40- Every resource belongs to an `Organization`
41- The `organizationId` is validated on **every** DB query — never skip this
42- Use the `withOrg(organizationId)` Prisma extension (in `packages/db`) which auto-appends the tenant filter
43- Row-level security is enforced in Postgres as a secondary safety net
44
45## Stripe integration
46- **Checkout**: `POST /api/billing/create-checkout` → redirect to Stripe
47- **Portal**: `POST /api/billing/create-portal` → redirect to Stripe
48- **Webhooks**: `POST /api/webhooks/stripe` — always verify the signature
49- Subscription status is cached in `Organization.subscriptionStatus`
50- Re-sync via `npm run billing:sync` if the cache drifts
51
52## Critical rules
531. Never access DB without a tenant filter (`organizationId`)
542. Always verify Stripe webhook signatures before trusting event data
553. Never log PII (emails, names) in production
564. API tokens (Bearer) and session cookies are separate auth flows — don't conflate them
575. Use `packages/ui` components; don't create one-off UI in `apps/web`
58
59## Environment variables (root `.env`)
60```
61DATABASE_URL=
62NEXTAUTH_SECRET=
63GITHUB_ID=
64GITHUB_SECRET=
65STRIPE_SECRET_KEY=
66STRIPE_WEBHOOK_SECRET=
67RESEND_API_KEY=
68NEXT_PUBLIC_APP_URL=http://localhost:3000
69```
70