如何解决常见的 Prisma ORM 错误:调试技巧和最佳实践

对于希望为数据库上执行的查询添加类型安全性的每个软件工程师、独立制造商和 SaaS 创始人来说,Prisma ORM 是一个强大的伴侣。

我已经使用 Prisma 两年了,并且有解决不同类型错误的经验。

在本文中,我想与您分享这一点,这样您就不必花费数小时来弄清楚我已经学到的内容。

为什么选择 Prisma?

我一直将 Prisma 用于我的所有 SaaS 产品。到目前为止,我已经构建了六款 SaaS 产品,我希望将来能构建更多产品(其中一些是 BlackTwist、Wuf、Userdesk、Inboxs、Hivoe)。

Prisma ORM 也是我构建的 SaaS Boilerplate 的一部分,称为 Shipped.club。

原因很简单,我希望您利用我有经验的工具,以便我可以帮助购买后可用的私人 Discord 社区中的客户。

使用 Prisma 的主要目标是:

  • 控制你的数据库模式定义表和类型应用迁移使用类型安全查询数据库避免输入错误
  • 防止 SQL 注入
  • 杠杆池
  • 常见问题

    尽管这是一个稳定的产品,但有时您仍会遇到一些问题。

    下面我描述两个常见的错误:

  • 迁移错误
  • 查询错误
  • 让我们从第一个开始

    数据迁移错误

    有时您的迁移会发出错误,这不太容易调试,就像这个一样。

    Error: ERROR: prepared statement "s2" does not exist

    当您运行“npx prisma db push”(将您的架构更改应用于数据库)或“npx prisma generate”(从您的架构生成类型)时,通常会收到此错误。

    但这意味着什么?

    使用关系数据库(如 PostgreSQL(市场上最流行、最先进的开源 SQL 数据库之一))时会发生此错误。

    具体来说,PostgreSQL 中的预处理语句是一种功能,它允许您执行由数据库服务器准备并存储以供重复使用的查询计划。它可以提高性能和安全性,尤其是在使用不同参数值多次执行相同查询时。

    Prisma ORM 在底层利用准备好的语句作为其数据库查询执行过程的一部分(但您不必知道或掌握准备好的语句即可使用 Prisma)。

    主要问题在于不同的数据库连接字符串配置、池和 PgBouncer 之间的混淆。

    让我直接讲解决方案,然后我会向您解释不同的部分。

    我发现的主要解决方案是使用两个连接字符串,一个用于运行迁移,另一个用于执行 Web 应用程序/node.js 服务器。

    首先,确定数据库的正确连接字符串。

    运行迁移的连接字符串是对数据库的直接访问(没有池或 PgBouncer),例如

    postgresql://username:password@host:5432/dbname

    将此连接字符串保存为名为“DATABASE_DIRECT_URL”的环境变量。

    DATABASE_DIRECT_URL="postgresql://username:password@host:5432/dbname"

    将其添加到您的本地 `.env` 文件和托管服务(Vercel、Netlify、Render 等)。

    其次,获取池的连接字符串,例如:

    postgresql://user:password@host:post/poolName

    从您的数据库托管提供商处获取此连接字符串,参数可能有所不同。

    将此连接字符串保存为名为“DATABASE_URL”的环境变量。

    DATABASE_URL="postgresql://user:password@host:post/poolName"

    将其添加到您的本地 `.env` 文件和托管服务(Vercel、Netlify、Render 等)。

    第二步是更新 Prisma 配置以使用这两个环境变量。

    打开文件“schema.prisma”并更新“datasource db”部分以包含以下内容:

    datasource db {
      provider  = "postgresql"
      url       = env("DATABASE_URL")
      directUrl = env("DATABASE_DIRECT_URL")
    }

    现在,在本地运行常用命令,如“npx prisma db push”和“npx prisma generate”,以确保一切正常,并在本地运行您的应用程序。

    如果一切顺利,请将修改推送到您的托管服务并触发新的部署以使修改生效。

    Prisma 现已正确配置,您不应该再遇到同样的问题。

    查询性能问题

    最近,我收到了“超出堆栈深度限制”错误消息。

    PrismaClientUnknownRequestError: Invalid `prisma.table.findMany()` invocation: Error occurred during query execution: ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresError { code: "54001", message: "stack depth limit exceeded", severity: "ERROR", detail: None, column: None, hint: Some("Increase the configuration parameter \"max_stack_depth\" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.") }), transient: false })

    这意味着内存使用量超出了特定查询的数据库配置。

    我在数据库上运行了此 SQL 命令并发现了以下值:

    SHOW max_stack_depth;
    ---
    2MB

    我有一个查询,它返回了 1900 多条记录来汇总一些值。

    对于每条记录,我必须重新计算某些值的总和。

    发生这种情况时,您有两个选择:

  • 增加数据库中的 max_stack_depth 值
  • 重构代码中的逻辑以避免运行大查询
  • 优化查询
  • 我尝试增加“max_stack_depth”,因为这是最快的解决方案(不是最好的解决方案),但我的数据库托管在 DigitalOcean 上,我没有运行“ALTER”命令的权限:

    ALTER SYSTEM SET max_stack_depth = '4MB';
    ---
    permission denied to set parameter "max_stack_depth"

    (我通过支持票联系了 DigitalOcean,他们很快就提供了解决方案,我可以使用他们的 Web API 来更改该值)。

    然后,我开始分析问题。

    我尝试做的第一件事是使用 pgAdmin 对数据库运行与普通 SQL 相同的查询,并且它完美地运行了。

    这很可疑,这意味着 Prisma 执行的查询可能过于复杂且昂贵。

    然后,我重构了代码,使用 Prisma 方法“$queryRaw”运行相同的查询,并且成功了!

    此时,我不想手动定义查询返回的值的所有类型,我想到使用 Prisma 的一个很酷的新特性,叫做 TypedSQL。

    这样,我就可以使用 Prisma 自动生成的类型对数据库运行原始查询。

    所以,最后,我重构了代码,避免增加“max_stack_depth”的值,因为这会增加内存消耗。

    结论

    我希望本文对您有所帮助,并避免您花费数小时使用 Prisma 来解决此类错误。

    总的来说,我对该工具的体验非常好而且令人兴奋,而且我看到他们正在发布新版本、新功能和改进。

    您可以通过此链接找到 Prisma 的文档。

    顺便说一句,我与 Prisma 没有任何关系,我只是该产品的满意用户。

    干杯,

    卢卡