monorepo 共享数据表 schema


在 Monorepo 部署到 Vercel 时出现 Module not found,通常是因为以下三个原因之一:

  1. 依赖类型错误:将共享包放到了 devDependencies 而不是 dependencies(Vercel 生产环境构建会剔除 devDeps)。
  2. 构建顺序问题:NestJS 开始构建时,packages/database 还没有编译完成(没有生成 dist)。
  3. 入口文件配置错误package.jsonexports/main/types 指向了错误的文件。

第一步:配置共享包 packages/database

核心原则:先编译,后引用。我们需要把 TS 编译成 JS 给 NestJS 运行时使用,同时保留 .d.ts 给 TS 类型检查。

1. packages/database/package.json

请严格检查你的 exportsscripts 配置:

{
  "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 项目设置正确:

  1. Framework Preset: NestJS (如果没选,选 Other 并在 Output Directory 填 dist)。
  2. Build Command: cd ../.. && npx turbo build --filter=api... (通常 Vercel 会自动检测 Turbo,你只需要运行 pnpm build 即可)。
  3. Root Directory: apps/api

最重要的一点: Vercel 部署时,会在根目录执行 pnpm install。如果你的 @repo/dbworkspace:*,pnpm 会自动创建符号链接。 然后 Vercel 执行 Build 命令。由于我们配置了 Turbo 的 dependsOn: ["^build"],它会自动去构建 packages/database

如果还是报错? 检查 Vercel 的环境变量设置。确保没有设置 NPM_FLAGS=--production 或者是导致 devDependencies (包括 turbo) 没被安装的设置。


验证方案是否成功

  1. 删除 apps/api 里所有临时的 schema 文件。
  2. 清理:在根目录运行 rm -rf node_modules packages/*/dist packages/*/node_modules apps/*/node_modules
  3. 安装pnpm install
  4. 构建:在根目录运行 pnpm build
    • 观察控制台输出,你应该看到 @repo/db:build 先于 api:build 执行。
    • 检查 packages/database/dist 目录是否存在。
  5. 运行cd apps/api && pnpm start:dev