monorepo 共享数据表 schema
在 Monorepo 部署到 Vercel 时出现 Module not found,通常是因为以下三个原因之一:
- 依赖类型错误:将共享包放到了
devDependencies而不是dependencies(Vercel 生产环境构建会剔除 devDeps)。 - 构建顺序问题:NestJS 开始构建时,
packages/database还没有编译完成(没有生成 dist)。 - 入口文件配置错误:
package.json的exports/main/types指向了错误的文件。
第一步:配置共享包 packages/database
核心原则:先编译,后引用。我们需要把 TS 编译成 JS 给 NestJS 运行时使用,同时保留 .d.ts 给 TS 类型检查。
1. packages/database/package.json
请严格检查你的 exports 和 scripts 配置:
{
"name": "@workspace/db",
"version": "0.0.1",
"private": true,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
"db:generate": "drizzle-kit generate",
"db:push": "drizzle-kit push"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.4.1",
"dependencies": {
"drizzle-orm": "^0.44.7",
"drizzle-zod": "^0.8.3",
"pg": "^8.16.3",
"zod": "^3.25.76"
},
"devDependencies": {
"dotenv": "^17.2.3",
"drizzle-kit": "^0.31.7",
"tsup": "^8.5.1",
"typescript": "^5.9.3"
}
}
2. packages/database/tsup.config.ts (可选,或使用上面的 CLI 参数)
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'], // NestJS(CommonJS) 和 Frontend(ESM) 都能用
dts: true, // 生成类型文件
splitting: false,
sourcemap: true,
clean: true,
});
3. packages/database/src/index.ts
// 统一导出所有内容
export * from './schema';
export * from './zod'; // 导出 drizzle-zod 生成的 schema
// 如果有 DB 连接实例,也可以导出,但通常建议只导出类型和 Schema 定义
第二步:配置 NestJS 项目 (apps/api)
这是最容易出错的地方。
1. apps/api/package.json
关键点: @repo/db 必须在 dependencies 中,不能在 devDependencies 中。因为 NestJS 运行时需要里面的 JS 代码。
{
"name": "api",
"dependencies": {
"@repo/db": "workspace:*", // <--- 必须在这里!
"@nestjs/common": "^10.0.0",
"zod": "^3.22.4",
"nestjs-zod": "^3.0.0"
// ... 其他依赖
},
"devDependencies": {
// ...
}
}
2. apps/api/tsconfig.json (可选,但推荐)
为了让 IDE(VSCode)能直接跳转到源码而不是 dist,以及防止 TypeScript 报错,建议配置 paths。
{
"compilerOptions": {
"paths": {
"@repo/db": ["../../packages/database/src/index.ts"]
// 这里的路径指向 src,是为了开发体验(点击跳转源码)
// 实际运行时,Node 会根据 package.json 的 exports 去找 dist
}
}
}
注意:虽然配置了 paths 指向 src,但因为我们使用了 tsup 构建,NestJS 构建时实际上会引用 node_modules/@repo/db (即 dist)。这能保证开发体验和运行时的平衡。
第三步:配置构建管道 (Turbo.json)
这是解决 Vercel 部署报错的终极方案。你需要告诉构建系统:在构建 App 之前,必须先构建 DB 包。
在根目录的 turbo.json:
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"], // <--- 魔法在这里!
"outputs": ["dist/**", ".next/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
^build的意思是:执行当前项目的 build 之前,先执行所有依赖项(dependencies)的 build 命令。- 当你运行
pnpm build(在 api 目录) 或者turbo build(在根目录) 时,Turbo 会检测到api依赖@repo/db,于是它会先去 packages/database 执行 tsup build,生成 dist 文件,然后再构建 api。
第四步:Vercel 部署设置
确保你的 Vercel 项目设置正确:
- Framework Preset: NestJS (如果没选,选 Other 并在 Output Directory 填
dist)。 - Build Command:
cd ../.. && npx turbo build --filter=api...(通常 Vercel 会自动检测 Turbo,你只需要运行pnpm build即可)。 - Root Directory:
apps/api。
最重要的一点:
Vercel 部署时,会在根目录执行 pnpm install。如果你的 @repo/db 是 workspace:*,pnpm 会自动创建符号链接。
然后 Vercel 执行 Build 命令。由于我们配置了 Turbo 的 dependsOn: ["^build"],它会自动去构建 packages/database。
如果还是报错?
检查 Vercel 的环境变量设置。确保没有设置 NPM_FLAGS=--production 或者是导致 devDependencies (包括 turbo) 没被安装的设置。
验证方案是否成功
- 删除
apps/api里所有临时的 schema 文件。 - 清理:在根目录运行
rm -rf node_modules packages/*/dist packages/*/node_modules apps/*/node_modules。 - 安装:
pnpm install。 - 构建:在根目录运行
pnpm build。- 观察控制台输出,你应该看到
@repo/db:build先于api:build执行。 - 检查
packages/database/dist目录是否存在。
- 观察控制台输出,你应该看到
- 运行:
cd apps/api && pnpm start:dev。