Files
everything-claude-code/examples/saas-nextjs-CLAUDE.md
Affaan Mustafa b57eef4f71 docs: improve README with agent guide, FAQ, and fix component counts
- Fix inaccurate counts: 13 agents (was 15+), 34 skills (was 30+), 31 commands (was 30)
- Add "Which Agent Should I Use?" decision table with common workflows
- Add FAQ section addressing top recurring issues (hooks, context window, cross-platform)
- Add 5 missing skills and 7 missing commands to directory tree listing
- Expand code-reviewer agent with React/Next.js, Node.js patterns, and confidence filtering
- Add real-world SaaS example (Next.js + Supabase + Stripe) in examples/
2026-02-12 13:24:24 -08:00

4.7 KiB

SaaS Application — Project CLAUDE.md

Real-world example for a Next.js + Supabase + Stripe SaaS application. Copy this to your project root and customize for your stack.

Project Overview

Stack: Next.js 15 (App Router), TypeScript, Supabase (auth + DB), Stripe (billing), Tailwind CSS, Playwright (E2E)

Architecture: Server Components by default. Client Components only for interactivity. API routes for webhooks and server actions for mutations.

Critical Rules

Database

  • All queries use Supabase client with RLS enabled — never bypass RLS
  • Migrations in supabase/migrations/ — never modify the database directly
  • Use select() with explicit column lists, not select('*')
  • All user-facing queries must include .limit() to prevent unbounded results

Authentication

  • Use createServerClient() from @supabase/ssr in Server Components
  • Use createBrowserClient() from @supabase/ssr in Client Components
  • Protected routes check getUser() — never trust getSession() alone for auth
  • Middleware in middleware.ts refreshes auth tokens on every request

Billing

  • Stripe webhook handler in app/api/webhooks/stripe/route.ts
  • Never trust client-side price data — always fetch from Stripe server-side
  • Subscription status checked via subscription_status column, synced by webhook
  • Free tier users: 3 projects, 100 API calls/day

Code Style

  • No emojis in code or comments
  • Immutable patterns only — spread operator, never mutate
  • Server Components: no 'use client' directive, no useState/useEffect
  • Client Components: 'use client' at top, minimal — extract logic to hooks
  • Prefer Zod schemas for all input validation (API routes, forms, env vars)

File Structure

src/
  app/
    (auth)/          # Auth pages (login, signup, forgot-password)
    (dashboard)/     # Protected dashboard pages
    api/
      webhooks/      # Stripe, Supabase webhooks
    layout.tsx       # Root layout with providers
  components/
    ui/              # Shadcn/ui components
    forms/           # Form components with validation
    dashboard/       # Dashboard-specific components
  hooks/             # Custom React hooks
  lib/
    supabase/        # Supabase client factories
    stripe/          # Stripe client and helpers
    utils.ts         # General utilities
  types/             # Shared TypeScript types
supabase/
  migrations/        # Database migrations
  seed.sql           # Development seed data

Key Patterns

API Response Format

type ApiResponse<T> =
  | { success: true; data: T }
  | { success: false; error: string; code?: string }

Server Action Pattern

'use server'

import { z } from 'zod'
import { createServerClient } from '@/lib/supabase/server'

const schema = z.object({
  name: z.string().min(1).max(100),
})

export async function createProject(formData: FormData) {
  const parsed = schema.safeParse({ name: formData.get('name') })
  if (!parsed.success) {
    return { success: false, error: parsed.error.flatten() }
  }

  const supabase = await createServerClient()
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) return { success: false, error: 'Unauthorized' }

  const { data, error } = await supabase
    .from('projects')
    .insert({ name: parsed.data.name, user_id: user.id })
    .select('id, name, created_at')
    .single()

  if (error) return { success: false, error: 'Failed to create project' }
  return { success: true, data }
}

Environment Variables

# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=     # Server-only, never expose to client

# Stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=

# App
NEXT_PUBLIC_APP_URL=http://localhost:3000

Testing Strategy

/tdd                    # Unit + integration tests for new features
/e2e                    # Playwright tests for auth flow, billing, dashboard
/test-coverage          # Verify 80%+ coverage

Critical E2E Flows

  1. Sign up → email verification → first project creation
  2. Login → dashboard → CRUD operations
  3. Upgrade plan → Stripe checkout → subscription active
  4. Webhook: subscription canceled → downgrade to free tier

ECC Workflow

# Planning a feature
/plan "Add team invitations with email notifications"

# Developing with TDD
/tdd

# Before committing
/code-review
/security-scan

# Before release
/e2e
/test-coverage

Git Workflow

  • feat: new features, fix: bug fixes, refactor: code changes
  • Feature branches from main, PRs required
  • CI runs: lint, type-check, unit tests, E2E tests
  • Deploy: Vercel preview on PR, production on merge to main