Prisma Client 扩展(预览版)开启了许多新的使用场景。本文将探讨如何使用扩展为 Prisma Client 添加自定义功能。
目录
简介
Prisma Client 扩展提供了一种功能强大的新方式,以类型安全的方式为 Prisma 添加功能。借助它们,您将能够为 ORM 尚未原生支持的问题创建简单、灵活的解决方案。您可以在 TypeScript 或 JavaScript 中定义扩展,组合它们,甚至创建多个带有不同扩展的轻量级 Prisma Client 实例。
准备就绪后,您可以将您的扩展以代码片段的形式分享给社区,或者通过打包并发布到 npm 来分享。本文将向您展示扩展的可能性,并希望启发您创建和分享自己的扩展!
注意:我们相信 Prisma Client 扩展将在使用 Prisma 时开辟许多新的可能性。然而,仅仅因为一个问题可以用扩展解决,并不意味着它将来不会通过一流的功能来解决。我们的目标之一是与社区一起实验和探索解决方案,然后再将它们原生集成到 Prisma 中。
使用 Prisma Client 扩展
要使用 Prisma Client 扩展,您需要首先在 Prisma schema 文件中启用 clientExtensions
预览功能
然后,您可以在 Prisma Client 实例上调用 $extends
方法。这将返回一个新的“扩展”客户端实例,而不会修改原始实例。您可以链式调用 $extends
来使用多个扩展,并创建带有不同扩展的独立实例
扩展的组件
扩展中可以包含四种不同类型的组件
- 模型(Model)组件允许您为模型添加新方法。这是在默认方法(如
findMany
、create
等)之外添加新操作的便捷方式。您可以将其用作常用查询方法的存储库,封装模型的业务逻辑,或执行您可能使用类上的静态方法进行的任何操作。 - 客户端(Client)组件可用于向 Prisma Client 本身添加新的顶级方法。使用此功能扩展客户端,使其具有不与特定模型绑定的功能。
- 查询(Query)组件允许您挂接到查询生命周期,并以类型安全的方式执行副作用、修改查询参数或更改结果。这些是 中间件 的替代方案,它们提供完整的类型安全,并且可以临时应用于不同的扩展客户端实例。
- 结果(Result)组件将自定义字段和方法添加到查询结果对象。这些允许您实现虚拟/计算字段,在一个地方定义模型实例的业务逻辑,并转换查询返回的数据。
单个扩展可以包含一个或多个组件,以及一个可选的名称,用于在错误消息中显示
要查看定义每种扩展组件的完整语法,请参阅文档。
共享扩展
您可以使用 Prisma.defineExtension
工具定义一个可以与其他用户共享的扩展
将共享扩展发布到 npm 时,我们建议使用 prisma-extension-<package-name>
命名约定。这将使用户更容易在其应用程序中找到并安装您的扩展。
例如,如果您发布一个包名为 prisma-extension-find-or-create
的扩展,用户可以这样安装它
然后在他们的应用程序中使用该扩展
阅读我们关于共享扩展的文档获取更多详细信息。
示例使用场景
我们整理了一系列可以用扩展解决的使用场景,并创建了一些如何编写这些扩展的示例。让我们看看这些使用场景及其实现
注意:Prisma Client 扩展仍处于预览阶段,并且下面的一些示例可能存在某些限制。已知限制会在示例的
README
文件中列出,位于 GitHub 上。
示例:计算字段
在 GitHub 上查看完整示例
此示例演示如何创建 Prisma Client 扩展,为 Prisma 模型添加虚拟/计算字段。这些字段不包含在数据库中,而是在运行时计算。
计算字段是类型安全的,可以返回从简单值到复杂对象,甚至是可作为模型实例方法的函数。计算字段必须指定它们依赖于哪些其他字段,并且可以由其他计算字段组合/重用。
示例:转换字段
在 GitHub 上查看完整示例
此示例演示如何使用 Prisma Client 扩展来转换 Prisma 查询返回结果中的字段。在此示例中,date
字段被转换为特定区域设置的相对字符串。
这展示了一种在应用程序数据访问层实现国际化 (i18n) 的方法。然而,这种技术可以使您对查询结果中的字段实现任何类型的自定义转换或序列化/反序列化。
示例:模糊化字段
在 GitHub 上查看完整示例
此示例是前面转换字段示例的特殊情况。它使用扩展来隐藏 User
模型上的敏感 password
字段。password
列不包含在底层 SQL 查询的选择列中,并且在用户结果对象上访问时将解析为 undefined
。它也可以解析为任何其他值,例如模糊化字符串,如 "********"
。
示例:实例方法
在 GitHub 上查看完整示例
此示例演示如何向 Prisma 结果对象添加类似Active Record的接口。它使用 result
扩展,将 save
和 delete
方法直接添加到 Prisma Client 方法返回的 User
模型对象。
此技术可用于通过行为自定义 Prisma 结果对象,类似于向模型类添加实例方法。
示例:静态方法
在 GitHub 上查看完整示例
此示例演示如何创建 Prisma Client 扩展,为 User 模型添加 signUp()
和 findManyByDomain()
方法。
此技术可用于抽象通用查询/操作的逻辑,创建类似仓库的接口,或执行您可能使用静态类方法进行的任何操作。
示例:模型过滤器
在 GitHub 上查看完整示例
此示例演示了一个 Prisma Client 扩展,它为模型添加了可重用过滤器,这些过滤器可以组合并传递给查询的 where
条件。复杂、常用的过滤条件可以编写一次,并通过扩展的 Prisma Client 实例在多个查询中访问。
示例:只读客户端
在 GitHub 上查看完整示例
此示例创建了一个只允许读取操作(如 findMany
和 count
),而不允许写入操作(如 create
或 update
)的客户端。调用写入操作将在运行时和 TypeScript 编译时导致错误。
示例:输入转换
在 GitHub 上查看完整示例
此示例创建了一个扩展客户端实例,它修改查询参数以仅包含 published
帖子。
由于 query
扩展允许修改查询参数,因此可以使用此方法应用各种默认过滤器。
示例:输入验证
在 GitHub 上查看完整示例
此示例使用 Prisma Client 扩展在创建和更新数据库对象时执行自定义运行时验证。它使用 Zod 运行时模式来检查传递给 Prisma 写入方法的数据是否有效。
这可用于清理用户输入或拒绝不符合您的业务逻辑规则定义的某些条件的突变。
示例:JSON 字段类型
在 GitHub 上查看完整示例
下一个示例结合了输入验证和转换字段示例中展示的方法,为 Json
字段提供静态和运行时类型。它使用 Zod 解析字段数据并推断静态 TypeScript 类型。
此示例包含一个带有 JSON 配置文件字段的 User
模型,该字段具有稀疏结构,可能因用户而异。该扩展分为两部分
- 一个
result
扩展,添加一个计算的profile
字段。该字段使用Profile
Zod 模式来解析底层无类型的profile
字段。TypeScript 从解析器推断静态数据类型,因此查询结果同时具有静态和运行时类型安全。 - 一个
query
扩展,用于解析User
模型的写入方法(如create
和update
)的输入数据中的profile
字段。
示例:查询日志
在 GitHub 上查看完整示例
此示例演示如何使用 Prisma Client 扩展来执行与中间件相似的任务。在此示例中,一个 query
扩展会跟踪完成每个查询所需的时间,并记录结果以及查询和参数本身。
此技术可用于执行通用日志记录、发出事件、跟踪使用情况等。
注意:您可能对OpenTelemetry 追踪和指标功能(均处于预览阶段)感兴趣,它们提供了有关性能以及 Prisma 如何与数据库交互的详细见解。
示例:重试事务
在 GitHub 上查看完整示例
此示例演示如何使用 Prisma Client 扩展来自动重试因写入冲突/死锁超时而失败的事务。失败的事务将使用指数退避和抖动重试,以减少高流量下的争用。
示例:无回调交互式事务
在 GitHub 上查看完整示例
此示例展示了一个 Prisma Client 扩展,它添加了一个新的 API,用于启动交互式事务而无需回调。
这赋予您交互式事务(例如读-修改-写周期)的全部功能,但采用更命令式的 API。在某些情况下,这可能比交互式事务的常规回调式 API 更方便。
示例:审计日志上下文
在 GitHub 上查看完整示例
此示例演示如何使用 Prisma Client 扩展将当前应用程序用户的 ID 作为上下文提供给 Postgres 中的审计日志触发器。用户 ID 包含在跟踪表中每一行更改的审计跟踪中。
此解决方案的详细说明可在GitHub 上示例的 README
中找到。
示例:行级安全
在 GitHub 上查看完整示例
此示例演示如何使用 Prisma Client 扩展,通过 Postgres 中的行级安全 (RLS) 在多租户应用程序中隔离租户之间的数据。
此解决方案的详细说明可在GitHub 上示例的 README
中找到。
告诉我们您的想法
我们希望您和我们一样对 Prisma Client 扩展所创造的可能性感到兴奋!
💡 您可以在这个 GitHub issue 中与我们分享您的反馈。
不要错过下一篇文章!
订阅 Prisma 新闻通讯