在 Prisma API 中没人谈论的问题
Source: Dev.to
重复查询逻辑问题
随着需求的增长,端点需要:
- 过滤
- 搜索
- 排序
- 分页
- 软删除检查
- 查询校验
典型的临时解决方案看起来像这样:
if (query.isActive) {
where.isActive = query.isActive === "true";
}
if (query.search) {
where.OR = [
{ firstName: { contains: query.search, mode: "insensitive" } },
{ email: { contains: query.search, mode: "insensitive" } }
];
}
if (query.sort === "createdAt") {
orderBy.createdAt = "desc";
}
每个端点最终都会拥有自己稍有不同的同一套逻辑。
起初这种重复看似无害,但久而久之会出现:
- 一个端点允许无效过滤器,另一个却拒绝它们
- 排序行为不一致
- 布尔值在某些地方被正确处理,在其他地方却被当作字符串
这些细微的不一致会导致长时间的调试,即使实际上没有任何技术上的错误。
为什么简单的助手函数不够
常见的做法是把重复代码抽取到一个助手函数中。实际上,这类助手往往会变成:
- 过于灵活 —— 不安全且难以推理
- 过于严格 —— 无法在不同端点之间复用
- 难以扩展 —— 当出现新需求时容易脆弱
有些助手甚至会直接执行 Prisma 查询,导致服务层失去对执行流程的控制。
用意图编码而不是重复逻辑
真正的问题不在于查询本身,而是缺少一种明确的方式来 编码意图——哪些字段可以被过滤、搜索、排序或拒绝。
于是出现了 Prisma Query Builder(prisma‑qb),一个小型库,允许你一次性配置这些规则,并生成安全的 Prisma 查询对象。
使用示例
import { buildPrismaQuery } from "prisma-qb";
const { where, orderBy } = buildPrismaQuery({
query: req.query,
searchFields: [
{ field: "firstName" },
{ field: "email" }
],
filterFields: [
{ key: "isActive", field: "isActive", type: "boolean" }
],
sortFields: [
{ key: "createdAt", field: "createdAt" }
],
defaultSort: { key: "createdAt", order: "desc" }
});
该函数仅返回 Prisma 的 where 和 orderBy 对象——不执行查询。你的服务层仍然负责实际运行查询。
好处
- 可预测的 API —— 无效的查询参数、意外的过滤以及未检查的排序会立即被拦截。
- 类型感知的搜索 —— 数字不会被误当作字符串。
- 没有隐藏的执行 —— 库永远不会为你运行 Prisma,因而你保持完整控制。
- 对混乱输入的稳健 —— 只有你明确允许的字段会参与查询。
它不包装 Prisma,也不隐藏 Prisma,更不会发明新的查询概念;它只是负责任地构建 Prisma 查询。
谁该使用它?
- 构建 真实 API 的开发者,而非仅仅演示项目
- 重视 长期一致性 的团队
- 任何 讨厌在各端点重复相同逻辑 的人
- 需要让服务 可读且易于维护 的项目
如果这些描述让你产生共鸣,你可能已经体会到重复查询处理的痛苦。Prisma 解决了数据库访问;Prisma Query Builder 解决了 API 层剩余的重复工作。
获取方式
试一试吧,如果你不喜欢,欢迎告诉我。反馈有助于打造更好的工具。 😉