server-only 防止
密钥泄漏到客户端 bundle)。这页讲清楚装了什么、各自的职责、以及踩过的坑。
前置条件
- Node.js 20 或更新(看
package.json的engines)。 - pnpm 10(
corepack enable && corepack prepare [email protected] --activate)。 - 一个 Postgres 数据库,用于
db:push/db:migrate。
工具栈
Biome 2 —— 一个 binary 同时做 lint 和 format
用一个 Rust binary 替代 ESLint + Prettier,速度大约快 10 倍。配置在biome.json。
biome.json 的 linter.rules 调。
Knip 6 —— 死代码检测
找出无用 export、未使用的依赖、孤儿文件。重构后跑一次。knip.config.ts。误报加到 ignore / ignoreDependencies 列表。
Drizzle ORM 0.45 —— 类型安全 SQL
Schema 拆成src/db/{auth,app,affiliate,ai,license}.schema.ts。ID 用 nanoid
前缀的 text(如 lic_xxx),不用 serial。
Vitest 4 —— 单元测试
测试放在tests/unit/** 和 src/**/*.{test,spec}.ts。Node 环境、开了 globals、
@/ 别名可用。
next-safe-action 三层 —— 类型化的 server action
所有写操作走src/lib/safe-action.ts 里的三个 client 之一:
server-only —— 防止客户端泄漏的运行时守卫
碰 DB 或密钥的模块在文件首行import 'server-only'。如果不小心被 client
组件 import,构建会以清晰的报错失败,而不是把 DATABASE_URL 打进浏览器。
已经守护的:src/db/index.ts、src/lib/auth.ts、src/payment/provider/*、
src/credits/server.ts、src/license/index.ts。
@t3-oss/env-nextjs —— 类型化 env 变量
src/env.ts 用 Zod 声明每个必需和可选的 env 变量。缺必需变量时 app 拒绝
启动,配置错误在构建时就报,而不是在凌晨三点的生产环境。
永远从 @/env 引(不要 process.env.X),这样有类型和校验。
验证生效
每次 commit 前:常见坑
- 新文件忘了
import 'server-only'。任何读env.STRIPE_SECRET_KEY、 查 DB、调用密钥的文件,第一行就加。构建会抓泄漏。 - 用
process.env.X而不是env.X。你跳过了 Zod 校验、丢了类型, 变量名拼错时会在生产挂掉。Biome 没有规则禁止它——靠 code review 或grep -r 'process\.env\.'。 - 未登录路由调
userActionClientaction。会抛UNAUTHORIZED返回 401。 要么外面套个 session 检查,要么换成actionClient(如果 action 真的是公开的)。 - 生产环境跑
pnpm db:push。它直接 diff schema 并应用破坏性变更(drop、 rename),不生成迁移文件。生产用db:generate+db:migrate,加--strict能在破坏性操作前要求确认。 - Knip 误报。Skill registry、动态 import 的 provider 在静态分析里看着像
没人用。把它们加到
knip.config.ts的ignore列表——别真删了文件。
官方文档
- Biome:biomejs.dev
- Knip:knip.dev
- Drizzle ORM:orm.drizzle.team
- Vitest:vitest.dev
- next-safe-action:next-safe-action.dev
- t3-env:env.t3.gg