SaaS 应用 — 项目 CLAUDE.md¶
Next.js + Supabase + Stripe SaaS 应用的实际示例。 请将其复制到项目根目录,并根据技术栈进行自定义。
项目概览¶
技术栈: Next.js 15(App Router), TypeScript, Supabase(认证 + 数据库), Stripe(计费), Tailwind CSS, Playwright(E2E)
架构: 默认使用服务器组件。客户端组件仅用于交互性。Webhook 使用 API 路由,变更操作使用服务器操作。
重要规则¶
数据库¶
- 所有查询都使用启用 RLS 的 Supabase 客户端 — 绝不绕过 RLS
- 迁移文件写入
supabase/migrations/— 不要直接修改数据库 - 使用显式列列表的
select(),而不是select('*') - 所有面向用户的查询都包含
.limit(),以防止无限制的结果
认证¶
- 服务器组件中使用
@supabase/ssr的createServerClient() - 客户端组件中使用
@supabase/ssr的createBrowserClient() - 受保护的路由需检查
getUser()— 不要仅依赖getSession()进行认证 middleware.ts中的中间件会在每个请求中更新认证令牌
计费¶
- Stripe Webhook 处理器放置在
app/api/webhooks/stripe/route.ts - 不要信任客户端的价格数据 — 始终从 Stripe 服务端获取
- 订阅状态通过由 Webhook 同步的
subscription_status列进行确认 - 免费套餐用户:3 个项目、100 次 API 调用/天
代码风格¶
- 代码和注释中不要使用表情符号
- 仅使用不可变模式 — 使用展开运算符,不要修改原值
- 服务器组件:没有
'use client'指令,没有useState/useEffect - 客户端组件:开头添加
'use client',保持最小化 — 将逻辑提取到 Hook 中 - 优先使用 Zod 模式对所有 API 路由、表单和环境变量输入进行验证
文件结构¶
src/
app/
(auth)/ # 認証ページ(ログイン、サインアップ、パスワード忘れ)
(dashboard)/ # 保護されたダッシュボードページ
api/
webhooks/ # Stripe、Supabaseウェブフック
layout.tsx # プロバイダー付きルートレイアウト
components/
ui/ # Shadcn/uiコンポーネント
forms/ # バリデーション付きフォームコンポーネント
dashboard/ # ダッシュボード固有のコンポーネント
hooks/ # カスタム Reactフック
lib/
supabase/ # Supabaseクライアントファクトリー
stripe/ # Stripeクライアントとヘルパー
utils.ts # 汎用ユーティリティ
types/ # 共有TypeScript型
supabase/
migrations/ # データベースマイグレーション
seed.sql # 開発用シードデータ
主要模式¶
API 响应格式¶
type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: string; code?: string }
服务器操作模式¶
'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 }
}
环境变量¶
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY= # サーバーのみ、クライアントに公開しない
# Stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
# アプリ
NEXT_PUBLIC_APP_URL=http://localhost:3000
测试策略¶
重要 E2E 流程¶
- 注册 → 邮箱验证 → 创建第一个项目
- 登录 → 仪表盘 → CRUD 操作
- 升级套餐 → Stripe 结账 → 订阅生效
- Webhook:取消订阅 → 降级到免费套餐
ECC 工作流¶
# 機能の計画
/plan "Add team invitations with email notifications"
# TDDによる開発
/tdd
# コミット前
/code-review
/security-scan
# リリース前
/e2e
/test-coverage
Git 工作流¶
feat:新功能、fix:修复 bug、refactor:代码变更- 从
main创建功能分支,需要 PR - CI 中运行:Lint、类型检查、单元测试、E2E 测试
- 部署:PR 的 Vercel 预览,合并到
main时部署到生产环境
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC