在 TypeScript Node.js 项目中使用 PostgreSQL 部署 Prisma 7
修复 TypeScript 中常见的 Prisma 7 配置错误,包括字符串或未定义的 URL 问题以及 rootDir 相关故障,并使用 pg 驱动适配器连接 PostgreSQL。
来源: I Wasted 4 Hours Setting Up Prisma — Here’s How You Should Set Up Prisma v7
在普通的 TypeScript Node.js 项目中按照 Prisma 快速入门指南操作时,往往还没执行任何查询就会遇到大量编译错误。问题并不出在模型本身,而在于 TypeScript 编译器、Prisma 的新配置文件以及生成的客户端是如何与传统的 src/dist 目录结构相互作用的。本指南将解释你最可能遇到的三种错误、每种错误产生的原因,以及一套能顺利编译且基于 PostgreSQL 的逐步设置方法。Prisma 7 修改了多项默认设置,因此请查阅最新的 Prisma 文档,了解自本指南编写以来可能发生的变化。
导致新项目设置失败的三种错误
连接 URL 被输入为字符串或未定义
在 prisma.config.ts 中,数据库地址通常通过 process.env.DATABASE_URL 来获取。TypeScript 将每个环境变量定义为 string | undefined 类型,因为无法保证其在运行时已被设置。而配置文件期望的是普通的 string 类型,因此编译器会拒绝该赋值操作。
位于根目录外的 prisma.config.ts
一个典型的 TypeScript 项目会将 rootDir 设置为 src,outDir 设置为 dist。Prisma 会在项目根目录下生成 prisma.config.ts 文件,默认情况下编译器会包含它找到的所有 .ts 文件。随后它会报错,称有位于 rootDir 之外的文件也被纳入了程序中。将配置文件移到 src 目录可以消除该错误,但这并非正确的解决方法:src 是用于存放路由、控制器和数据访问等应用代码的,而非工具配置。
根目录之外的生成客户端
生成的客户端也会出现同样的问题。如果 Prisma 将其写入项目根目录下的 generated/prisma,编译器又会发现 rootDir 外存在 TypeScript 文件。将生成的代码移到 src 目录也不是明智之举,因为这些是来自 prisma generate 的构建输出,而非需要维护的源代码。
若不解决这些问题,项目将无法启动。另一个同样令人烦恼的副作用是:prisma.config.ts 的编译后的 JavaScript 文件可能会被保存在 dist 目录之外,从而让代码库变得杂乱无章。逐个修复错误往往还会引发新的问题,因此最好整体改进配置。
第一步:创建项目并安装依赖项
从一个空的 TypeScript 项目开始。安装编译器、用于直接运行 TypeScript 的 tsx,以及 Node 的类型定义,然后生成 tsconfig.json 文件:
npm init
npm install typescript tsx @types/node --save-dev
npx tsc --init
接下来,安装 Prisma。CLI 和 pg 类型定义属于开发依赖项;而客户端、PostgreSQL 驱动适配器、pg 驱动以及 dotenv 则属于运行时依赖项:
npm install prisma @types/pg --save-dev
npm install @prisma/client @prisma/adapter-pg pg dotenv
这里 @prisma/adapter-pg 包非常重要。在 Prisma 7 中,客户端通过 JavaScript 驱动适配器与数据库通信,因此需要传入已配置好的 pg 连接信息,而不能仅依赖架构文件中的 URL。
步骤 2:配置 ES 模块及编译器布局
在 package.json 中将该包标记为 ES 模块:
{
"type": "module"
}
接着在 tsconfig.json 中设置源代码目录和输出目录,然后创建 src 文件夹:
"rootDir": "./src",
"outDir": "./dist",
步骤 3:初始化 Prisma
运行一次 CLI 命令(单独运行时只会显示可用命令并确认安装正常):
npx prisma
然后为 PostgreSQL 初始化 Prisma:
npx prisma init --datasource-provider postgresql --output ../generated/prisma
这样会生成 prisma.config.ts 文件、包含 schema.prisma 的 prisma 文件夹,以及用于存储数据库 URL 的 .env 文件。--output 参数用于指定客户端代码的生成位置;步骤 4 会再次确认该选择。就在这一步之后,就会出现上述错误。
步骤 4:修复配置
通过三项针对性修改即可解决所有错误。
使用 Prisma 的 env 工具读取 URL
请将prisma.config.ts中的内容替换为以下内容。尽管代码片段中另有标注,但该文件实际上是用TypeScript编写的。
import "dotenv/config";
import type { PrismaConfig } from "prisma";
import { env } from "prisma/config";
export default {
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: "tsx prisma/seed.ts",
},
datasource: {
url: env("DATABASE_URL"),
},
} satisfies PrismaConfig;
需要注意的事项:
import "dotenv/config"会在任何代码读取之前加载.env文件。在Prisma 7的这种配置下,它不会再自动加载该文件。prisma/config中的env("DATABASE_URL")方法会返回一个string类型,如果该变量不存在则会抛出明显错误,从而避免了string | undefined类型的错误。satisfies PrismaConfig会对对象进行类型检查,而不会将其类型扩大。- 该配置还指定了模式文件和迁移文件的存放位置,以及用于初始化数据库的命令。
限制编译器包含的内容
在 tsconfig.json 的末尾添加一个 include 条目:
"include": ["src"],
这样就能告诉编译器仅编译 src 目录下的文件。Prisma CLI 仍会单独读取 prisma.config.ts,但 tsc 不会再将其视为程序的一部分,因此 rootDir 错误就会消失,且不会有多余的 JavaScript 文件生成在 dist 目录之外。
将客户端代码生成到 node_modules 中
最后,编辑 schema.prisma,让生成器没有自定义的输出路径:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
如果没有指定output,prisma-client-js生成器会将客户端代码写入node_modules目录,因此项目根目录中不会留下任何生成的内容,之前的rootDir错误也会随之消失。代码片段在数据源provider之后结束,需用大括号闭合该代码块。在Prisma 7中,连接URL位于prisma.config.ts文件中,而非模式文件中。
需要注意的是,这是一种权衡。Prisma较新的prisma-client生成器是以明确的输出目录为设计基础的,而prisma-client-js则是较旧的生成器。如果选择使用较新的版本,需将其输出目录置于src之外,并通过include选项将其排除在编译范围之外,如上所述。
第5步:添加数据库URL并执行迁移
将连接字符串放入 .env 文件中。下面的内容仅为占位符,请使用自己的数据库,并且绝不要提交真实值:
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
在 schema.prisma 中定义模型,然后创建并应用第一个迁移:
npx prisma migrate dev --name init
第6步:生成客户端
根据架构文件生成带类型的客户端:
npx prisma generate
每当架构发生变化时都需再次运行此操作,以确保类型与模型保持同步。
第7步:创建共享的 Prisma 实例
创建 src/lib/prisma.ts 文件,为整个应用程序保存一个客户端实例。该文件会根据连接字符串构建 PostgreSQL 适配器,并将其传递给 PrismaClient。这段代码也是用 TypeScript 编写的:
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../../node_modules/@prisma/client/index.js";
const connectionString = `${process.env.DATABASE_URL}`;
const adapter = new PrismaPg({ connectionString });
const prisma = new PrismaClient({ adapter });
export default prisma;
客户端通过明确的相对路径从 node_modules 中导入:
import { PrismaClient } from "../../node_modules/@prisma/client/index.js";
该路径有效是因为在步骤4中客户端已被生成到node_modules目录中。在大多数配置下,直接使用@prisma/client这个标识符也能指向同一个包,因此应先尝试这种方式;只有当模块解析设置要求时才使用显式路径。
这段代码中的一个隐患是:将process.env.DATABASE_URL放在模板字符串中会导致缺失值被转换为字面文本"undefined",进而引发令人困惑的连接错误。若能在变量不存在时立即抛出异常,就能让问题一目了然。
现在,在需要访问数据库的任何地方,比如控制器或服务类中导入这个客户端实例,然后通过它来执行CRUD操作。使用一个共享的客户端可以避免为每个文件创建新的连接池。
关键要点
- 在 TypeScript 中出现的绝大多数 Prisma 7 配置错误都源于编译器的
rootDir规则,而非 Prisma 本身。 - 使用
prisma/config中的env()函数结合dotenv/config,即可获得类型正确且已明确加载的数据库 URL。 - 通过
"include": ["src"]限制tsc的处理范围,这样工具文件和生成的代码就不会被纳入构建过程。 - 将生成的客户端代码放在
node_modules目录或被排除在外的输出文件夹中,切勿放入src目录。 - 创建一个使用
PrismaPg适配器的共享PrismaClient,并在启动时验证连接字符串。
如果您使用的是 Next.js 而非普通的 Node.js 服务,我们的在 Next.js 16 TypeScript 项目中配置 Prisma 7指南涵盖了该框架特有的相关细节。