PostGraphile 自定义变更(Custom Mutations):用 PostgreSQL 函数构建完整业务逻辑的 GraphQL Mutation
后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载导读本文围绕 PostGraphile v5 的「自定义变更Custom Mutations」机制展开讲解如何把业务逻辑封装进 PostgreSQL 函数并自动暴露为符合 Relay Mutation 规范的 GraphQL mutation。你将掌握自定义变更的识别规则、STRICT与SECURITY DEFINER的安全语义、pgStrictFunctions配置对参数可空性的影响以及批量插入等实战写法从而在自动 CRUD 变更之外写出真正贴合业务的自定义变更。一、为什么需要自定义变更PostGraphile 会为具备相应数据库权限的表自动生成 CRUD 变更Create / Update / Delete但现实中的业务逻辑几乎不可能只靠 CRUD 覆盖一条变更可能横跨多张表、涉及复杂的校验与权限判断、需要在同一个事务内完成一系列写入。也正因如此很多团队会直接关闭自动 CRUD 变更改用自定义变更实现全部写操作。关闭自动 CRUD 变更的方式是在 preset 中修改行为behaviorexport default { // ... schema: { defaultBehavior: -insert -update -delete, }, };自定义变更的核心思路是把业务逻辑写进一个 PostgreSQL 函数PostGraphile 在 introspection数据库结构自省阶段发现该函数后自动把它暴露为 GraphQL 的 mutation。这样你能访问数据库中的全部数据、完整使用 SQL 的能力事务、CTE、窗口函数、触发器联动等并把所有逻辑收拢在数据库这一层。PostGraphile 官方文档明确指出相当大比例的资深用户包括维护者本人几乎不使用自动 CRUD 变更而是通过三种方式自研 mutation数据库函数即本文的自定义变更Schema 扩展extendSchema自定义插件plugins。你可以按团队熟悉的技术栈任选其一。偏好 JavaScript如果你希望用 JavaScript/TypeScript 编写变更逻辑可以使用extendSchema直接建模它让你用简洁的 GraphQL SDL 构建出精确的 schema并通过 Grafastplan 精确控制变更执行时的每一步逻辑。这与数据库函数的方案互为补充——数据库函数适合「逻辑贴近数据」的场景extendSchema适合「逻辑贴近应用」的场景。二、自定义变更的识别规则Rules要让 PostGraphile 把某个数据库函数识别为自定义变更函数必须满足以下条件遵守通用的 PostGraphile 函数限制必须标记为VOLATILE这恰好是 PostgreSQL 函数的默认属性必须定义在被 introspect自省的 schema 中。关于第一条规则函数限制文档 明确了 PostGraphile 不支持的函数形态VARIADIC可变参数函数重载overloaded函数——目前无法在 GraphQL 上优雅地暴露同名重载返回裸record类型、且没有更多类型信息的函数——因为无法得知record包含哪些列也就无法转换为 GraphQL 类型。解决办法是把record改成用CREATE TYPE或类似方式定义的复合类型名。VOLATILE这一条件是有深意的GraphQL mutation 语义上代表「有副作用的写操作」而VOLATILE恰好表达了「函数可能返回不同结果、可能修改数据」因此只有易变函数才适合作为 mutation 暴露IMMUTABLE/STABLE函数则更可能被当作查询query字段处理。满足条件的函数在 GraphQL 中的呈现方式会兼容 Relay Input Object Mutations Specification即标准的 Relay 风格一个输入对象input返回一个以 mutation 名命名的 payload 对象。例如create function my_function(a int, b int) returns text as $$ … $$ language sql volatile;对应的 GraphQL 调用mutation { myFunction(input: { a: 1, b: 2 }) { text } }可以看到函数参数被包装进input对象函数返回值text直接成为 payload 上的字段。具体的参数名、类型与可空性可以在 Ruru / GraphiQL 的文档面板中查看。三、完整示例acceptTeamInvite下面是一个典型的多表业务场景——「接受团队邀请」。PostGraphile 会为下面的函数生成acceptTeamInvitemutationcreate function app_public.accept_team_invite(team_id integer) returns app_public.team_members as $$ update app_public.team_members set accepted_at now() where accepted_at is null and team_members.team_id accept_team_invite.team_id and member_id app_public.current_user_id() returning *; $$ language sql volatile strict security definer;这段函数演示了几个非常重要的 PostgreSQL 函数特性逐条说明如下。STRICT严格模式STRICT是可选的。它的含义是只要任意一个参数为NULL函数体就不会被调用直接返回NULL不报错。在本例中team_id是唯一参数加上STRICT后GraphQL 会把teamId标记为必填非空参数。SECURITY INVOKER 与 SECURITY DEFINER安全上下文SECURITY INVOKER是默认值函数以**调用者invoker**的身份权限执行。这意味着行级安全RLS、GRANT 权限等依然生效调用者只能影响自己有权限操作的数据。SECURITY DEFINER函数以**定义者definer**的身份权限执行通常是数据库属主。这意味函数可以绕过 RLS、RBAC 以及其他安全限制——请务必谨慎使用官方文档明确把它比作sudo权限极大一旦函数内部存在 SQL 注入或其他缺陷后果将是灾难性的。在上面的例子中使用SECURITY DEFINER的意图是即使调用者没有直接UPDATE team_members的权限也能通过这个精心封装、内部强制约束了member_id app_public.current_user_id()的函数完成「只接受自己收到的邀请」这一受限操作。安全边界被收窄并固化在函数内部而非依赖调用者的裸表权限。LANGUAGE 选择LANGUAGE sql纯 SQL适合简单的声明式逻辑也是本例所用的语言LANGUAGE plpgsql需要变量、循环、IF分支等过程式逻辑时使用LANGUAGE plv8JavaScript需要安装扩展适合偏好 JavaScript 语法的团队也可以使用 PostgreSQL 内置的其他过程语言如 Python、Perl、Tcl。选择原则逻辑越简单越优先用SQL涉及控制流时再升级到plpgsql等过程式语言。四、pgStrictFunctions把「无默认值参数」统一视为必填默认情况下PostGraphile 会根据函数是否声明STRICT来决定参数是否必填。如果你希望所有函数参数都被当作必填非空——除非该参数带有默认值——可以开启preset.gather.pgStrictFunctionsexport default { // ... gather: { pgStrictFunctions: true, }, };这与把函数标记为STRICT效果类似但有一个微妙的差别带默认值的参数可以被显式指定为NULL而不必让整个函数返回NULL。开启后无默认值的参数 → 必填Int!等非空类型有默认值的参数 → 可选Int等可空类型。例如create function foo(a int, b int, c int 0, d int null) ...会生成 mutationfoo(a: Int!, b: Int!, c: Int, d: Int)——a、b必填c、d可选。配置项与源码依据在仓库的配置参考文档 config/reference.mdx 中该配置被描述为gather.pgStrictFunctions— Type:boolean | undefined— If true, well treat all arguments that dont have defaults as being required.从源码实现看v4 preset 的makeV4Preset会把graphileBuildOptions.pgStrictFunctions解构出来并原样写入返回 preset 的gather.pgStrictFunctions字段最终交由 dataplan-pg 的 gather 阶段消费——也就是说它影响的是「自省函数参数、构建 GraphQL 输入类型」这一环节的可空性推导而不是执行期行为。仓库中还提供了对应的测试用例pgStrictFunctions.test.ts它通过core.test以gather: { pgStrictFunctions: true }生成并打印完整 schema 快照验证该配置对参数类型标注的影响。五、批量插入Bulk Insert示例自定义变更天然适合返回集合的场景。下面的函数演示「批量插入」一次调用插入num条记录并以集合SETOF形式返回全部新记录create function app_public.create_documents(num integer, type text, location text) returns setof app_public.document as $$ insert into app_public.document (type, location) select create_documents.type, create_documents.location from generate_series(1, num) i returning *; $$ language sql strict volatile;要点解析returns setof app_public.document返回一张表的记录集合PostGraphile 会将其映射为对应的 GraphQL 类型集合connection 或 list取决于你的集合配置insert ... select ... from generate_series(1, num) i利用generate_series生成 1 到num的行序列从而在一次 SQL 语句中插入多条记录比循环逐条插入高效得多strict volatileSTRICT保证num、type、location任一为空时直接返回NULL从而被标记为必填参数VOLATILE满足自定义变更的识别规则。六、安全与工程实践建议优先保持SECURITY INVOKER让 RLS 和 GRANT 继续发挥作用权限边界由数据库统一管理。只有确实需要「受限调用者执行属主级操作」时才使用SECURITY DEFINER。使用SECURITY DEFINER时的自查清单函数内部是否对输入做了严格校验类型、长度、取值范围是否通过current_user_id()等机制把数据访问范围锁定到当前用户是否只暴露最小必要的 SQL 能力避免任何动态拼接 SQL是否考虑过并发与事务隔离级别的影响善用STRICT/pgStrictFunctions表达 API 契约让必填参数在 GraphQL 类型系统层面就得到保证客户端无法绕过非空校验。在 Ruru / GraphiQL 中验证生成的 mutation函数一旦创建PostGraphile 重新 introspect 后即可在文档面板中看到生成的 mutation、输入参数、返回类型以及可空性标注作为「数据库函数 ↔ GraphQL 类型」映射的即时反馈。七、相关文档导航CRUD 变更如何关闭自动变更数据库函数限制VARIADIC / 重载 / recordSchema 扩展JavaScript 实现 mutation配置参考gather.pgStrictFunctions自定义查询Custom Queries与本文对称的查询侧机制赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐PostGraphile 自定义变更Custom Mutations用 PostgreSQL 函数编写业务级 MutationPostGraphile 自定义变更Custom Mutations用 PostgreSQL 函数编写业务级 Mutation PostGraphile后端API网关PostGraphile v4 自定义 Mutation 实战指南用 PostgreSQL 函数构建业务级 GraphQL 变更操作PostGraphile v4 自定义 Mutation 实战指南用 PostgreSQL 函数构建业务级 GraphQL 变更操作 导读 PostGraph后端API网关PostGraphile v4 自定义变更Custom Mutations实战指南用 PostgreSQL 函数编写精确业务变更PostGraphile v4 自定义变更Custom Mutations实战指南用 PostgreSQL 函数编写精确业务变更 PostGraphile后端API网关创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考