📌 项目地址:nextjs/saas-starter | ⭐ 15,833 颗星 | 🔧 TypeScript | 📜 MIT
这个仓库有 15833 个 star,多数人只把它当又一个启动模板。我按 README 跑了一遍,发现它真正值钱的地方不是那几页 shadcn 组件,而是支付与订阅状态的闭环设计。你会在本地开发时被迫理解 Stripe webhook 的意义、数据库迁移的顺序、中间件保护 Server Action 的方式——这些坑在大多数教程里被跳过,但这里全部用代码覆盖了。
支付流程必须在本地跑通,且不能跳过 stripe listen
README 描述得很简单:“Pricing page connects to Stripe Checkout” + “Subscription management with Stripe Customer Portal”。但本地调试时,你必须先登录 Stripe CLI:
stripe login
然后启动 webhook 转发:
stripe listen --forward-to localhost:3000/api/stripe/webhook
这一步不做,你在定价页点击“订阅”后 Stripe 不会通知本地后端,数据库的订阅状态不会更新,dashboard 也不会显示已订阅。很多新手以为“点了按钮就算支付集成完了”,实际差这一步整个闭环就断了。
测试付款用 Stripe 提供的测试卡:4242 4242 4242 4242,任何未来日期,任意三位 CVC。不会真的扣钱。
中间件分两层,全局保护路由、本地保护数据入口
README 原文:“Global middleware to protect logged-in routes” 和 “Local middleware to protect Server Actions or validate Zod schemas”。我看了代码后觉得这个设计比单纯用路由守卫更实用。
全局中间件在 middleware.ts 里检查 cookie 中的 JWT,未登录用户访问 dashboard 或 /api 受保护路由时被重定向到登录页。这是常见的做法。
但 Local middleware 才是亮点。项目在每个 Server Action 里调用了 validate(schema, data),用 Zod 校验输入,校验不通过直接抛错,不会写脏数据到数据库。比如 updateTeam 这个 Action,你在函数体里看到 validate(teamSchema, formData)。这种写法把校验从组件层挪到数据入口,改起来很爽——不用去页面里翻表单校验逻辑。
角色系统比较简单,只有 Owner 和 Member。README 说是 “Basic RBAC with Owner and Member roles”。够启动一个最小可用 SaaS,但如果你需要更细粒度(比如团队管理员、只读成员),得自己加表并在中间件里判断。项目提供了基础模式,扩展方向明确。
技术栈选型有具体理由,不是随便挑的
ORM 选了 Drizzle 而不是 Prisma。我试了下,Drizzle 生成的 SQL 可读性强,几乎没有隐式查询。例如联表统计用户订单这类操作,Prisma 可能产生 N+1,而 Drizzle 让你精确控制 JOIN 和 WHERE 语句。READM 没有说为什么,但我猜团队想要 SQL 级别的可预测性。
UI 库选了 shadcn/ui。它不是 npm 包,而是让你把组件代码复制到项目里。这样你改按钮颜色或间距时直接改文件就行,不会被库的版本更新绑架。而且搭配 Next.js Server Components,dashboard 里的静态卡片根本不需要发 JavaScript 到客户端。
本地开发的三条命令做了什么事
pnpm db:setup # 生成 .env 文件,引导你填写数据库连接和 Stripe 密钥
pnpm db:migrate # 执行数据库迁移(建表、建索引)
pnpm db:seed # 插入默认用户和团队
db:setup 会问你几个环境变量,填错的话后面启动不了。种子数据创建的用户是 test@test.com,密码 admin123,默认带一个团队。这意味着你甚至不用走注册流程就能直接登录看 dashboard。REREAME 里也写了:“You can also create new users through the /sign-up route.”
注意:这些命令需要你本地先有个 Postgres 实例。项目没有用 SQLite,所以你要自己启动 Postgres 或连一个远程数据库。
生产部署有明确步骤,但你要自己补细节
README 写了三个步骤:1)在 Stripe Dashboard 创建生产 webhook;2)部署到 Vercel;3)加环境变量。
实际部署时你会遇到几个问题:
- Stripe webhook 的签名密钥必须设置到环境变量里(通常是
STRIPE_WEBHOOK_SECRET),否则每次回调都会报签名错误,订阅状态永远不会更新。 - Vercel 部署后不会自动跑数据库迁移。你得在
package.json的postinstallhook 里加上pnpm db:migrate,或者在 Vercel 的build command里手动指定。不然生产环境数据库是空的。 - 如果你不用 Vercel(比如用 Railway 或自建服务器),要留意环境变量注入时机和数据库连接池的 max 连接数。Drizzle 默认不处理连接池,生产环境需要自己用
pg的池包装一下。
项目本身不集成邮件、分析、日志等。但它给了你钩子:/api/stripe/webhook 处理付款事件,activity logging system 记录用户行为。你可以在这两个地方插自己的业务逻辑。
这个模板适合谁
适合已经会用 Next.js 的开发者,特别是搞过支付但没完整走通过 webhook 到状态更新链路的人。它不是一个“开箱即用”的垂直产品,而是一个能让你跑通完整业务流的骨架。 你改代码时不会猜“这个函数为什么存在”,因为每一行都对应一个具体功能(路由保护、支付回调、数据校验)。15833 个 star 说明很多人认可这种设计方式。
如果你还不太清楚 App Router 的 layout.tsx 和 Server Actions 的区别,建议先看 Next.js 官方文档,再回来碰这个模板。