:用 PostgreSQL 函数编写业务级 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 会自动为数据库表生成 CRUD Mutations但真实业务往往需要更贴合领域逻辑的写操作——自定义 Mutations 让你把业务逻辑封装进 PostgreSQL 函数由 PostGraphile 自动内省并暴露为符合 Relay 规范的 GraphQL Mutation。阅读本文后你将掌握自定义 Mutation 的识别规则、STRICT/SECURITY DEFINER等关键属性语义、pgStrictFunctions配置以及批量插入这类典型实战写法。为什么需要自定义 MutationPostGraphile 的自动 CRUD Mutations参见 crud-mutations.md覆盖了绝大多数基础增删改场景但有两个现实问题真实业务逻辑校验、多表联动、权限判断、幂等处理很少能被单纯的单表 CRUD 表达许多团队甚至直接关闭自动 CRUD Mutations全部写操作走自定义函数。自定义 Mutation 的核心价值在于业务逻辑以 PostgreSQL 函数形式存在于数据库层PostGraphile 通过内省自动将其映射为 GraphQL Mutation你既能利用数据库的全部能力又能获得 GraphQL 的类型安全与 Relay 兼容性。更关键的是可以通过SECURITY DEFINER选择性地绕过 RLS 与 GRANT 检查——但这是把双刃剑使用前必须谨慎评估后文详述。偏好 JavaScript/TypeScript 实现如果你更想把变更逻辑写在 JS/TS 侧可以直接用extendSchema扩展 Schema用 Gra*fast* plans 精确控制 Mutation 的执行逻辑。两种方式各有适用场景本文聚焦数据库函数方案。自定义 Mutation 的识别规则PostGraphile 将 PostgreSQL 函数识别为自定义 Mutation需要同时满足以下条件遵守通用的函数限制详见 function-restrictions.md不支持VARIADIC可变参数函数不支持重载函数目前无法在 GraphQL 中整洁地暴露同名不同参的函数不支持返回无类型信息的record的函数因为无法得知record包含哪些列也就无法转换为 GraphQL——解决办法是把返回类型改成用CREATE TYPE或类似方式定义的复合类型名必须标记为VOLATILE这恰好是 PostgreSQL 函数的默认值——因为 Mutation 有副作用结果可能随时变化PostgreSQL 会避免对这类函数做优化裁剪必须定义在被内省的 schema 中默认是public及你通过 preset 配置的 schema。从源码结构看PostGraphile 依赖pg-introspection对pg_proc系统目录的内省结果来判定这些属性。在 utils/pg-introspection/src/introspection.ts 中可以看到内省模型直接对应 PostgreSQL 目录字段provolatile函数易变性i为 immutable、s为 stable、v为 volatileUse v also for functions with side-effects, so that calls to them cannot get optimized away——即带副作用必须用vproisstrict是否为 STRICT任一参数为 NULL 则函数不会被调用、直接返回 NULLprosecdef是否为 security definersetuid 函数proretset是否返回集合对应setof决定暴露为返回列表的 Mutationproparallel并行安全性。Relay 兼容的暴露形式满足上述规则的函数在 GraphQL 中的暴露形式与 Relay Input Object Mutations Specification 兼容所有入参收敛到一个input输入对象中函数返回的标量/复合类型对应到 Mutation 返回类型。例如如下 SQL 函数create function my_function(a int, b int) returns text as $$ … $$ language sql volatile;会生成myFunctionMutationGraphQL 调用形式为mutation { myFunction(input: { a: 1, b: 2 }) { text } }PostgreSQL 的函数名/参数名默认会被 PostGraphile 转换为 camelCase 的 GraphQL 字段名my_function→myFunctionteam_id→teamId。具体暴露了哪些参数可以直接在 Ruru/Graph*i*QL 的文档面板中查看 Mutation 类型上的input对象定义。实战示例接受团队邀请下面是一个完整的自定义 Mutation 示例它会生成 GraphQL 的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;函数中accept_team_invite.team_id这种写法是 PostgreSQL 对函数参数的引用方式函数名即参数记录名与表列名team_members.team_id形成清晰区分。关键属性逐项解析STRICT可选含义任一入参为 NULL 时函数根本不会被调用直接返回 NULL且不报错。效果PostGraphile 会据此把对应参数标记为 GraphQL 必填teamId: Int!在类型层面保证调用方必须传值。注意如果某些场景需要显式传 NULL就不应该用STRICT函数内部需自行处理 NULL 输入。SECURITY INVOKER默认值函数以调用者的安全上下文执行——即执行 GraphQL 请求的用户所对应的数据库角色。此时表上的 RLS、GRANT 权限正常生效是最安全的默认选择。SECURITY DEFINER函数以定义者通常是数据库所有者的安全上下文执行因此可能绕过 RLS、RBAC 及其他权限检查。使用它时请把它当作sudo一样谨慎对待只在确实需要提升权限的场景使用例如普通用户通过一个受控函数更新自己的记录但底层表不允许用户直接 UPDATE函数内部必须自己做必要的输入校验与边界检查因为权限屏障已被绕过避免在SECURITY DEFINER函数中引入可利用的注入点。LANGUAGE选择LANGUAGE sql示例所用简单、可读、适合单语句函数LANGUAGE plpgsql需要变量、循环、IF分支等过程式逻辑时使用LANGUAGE plv8可以用 JavaScript 编写需要安装plv8扩展PostgreSQL 内置的其他语言如 Python、Perl、Tcl 同样可用。函数返回复合类型示例返回app_public.team_members表对应的复合类型returning *会把更新后的整行返回给调用方。PostGraphile 会把该复合类型的各列暴露为 Mutation 返回对象的字段便于客户端一次取回变更后的数据避免二次查询。这也解释了前面规则中返回record必须有明确类型的原因——没有类型信息就无法生成返回对象的 GraphQL 字段。pgStrictFunctions全局收紧参数必填性默认情况下PostGraphile 按函数的实际定义推断参数是否必填。如果你希望除非参数有默认值否则一律视为必填可以开启preset.gather.pgStrictFunctionsexport default { // ... gather: { pgStrictFunctions: true, }, };它与给函数加STRICT标记类似但有微妙差异带默认值的参数仍可显式传 NULL而不需要整个函数返回 NULL。开启后无默认值的参数 → 必填有默认值的参数 → 可选。例如函数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可选。这在团队希望统一入参尽量必填、避免隐式 NULL 语义的 API 设计风格时非常实用。批量插入示例Bulk Insert自定义 Mutation 天然适合一次请求插入多条记录这类 GraphQL 原生 Mutation 不好表达的场景。下面的函数会插入num条记录并一次性返回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.documentproretset true表示返回记录集合PostGraphile 会把它暴露为返回列表[Document!]!之类的连接或列表类型的 Mutationgenerate_series(1, num)生成 1 到num的行配合insert ... select实现循环插入全部在数据库内完成避免客户端往返strict使num、type、location均为必填参数volatile必不可少——这是有副作用的写操作。验证与调试建议在 Ruru/Graph*i*QL 的文档浏览器中检查生成的 Mutation 及其input类型确认参数必填性、返回字段是否符合预期——这是排查命名转换与类型推断问题的第一现场仓库中的 PostGraphile 测试体系覆盖了大量 mutation 场景见 postgraphile/postgraphile/tests/mutations包含 50 组.sql/.graphql/.json5/.mermaid配对用例其中.graphql是查询、.sql是建表与函数定义、.json5是配置与预期结果可作为编写与调试自定义 Mutation 的参考范式若函数未被识别为 Mutation优先检查三条规则是否VOLATILE、是否定义在被内省的 schema 中、是否触碰了函数限制VARIADIC / 重载 / 无类型 record。总结PostGraphile 的自定义 Mutations 把业务逻辑放回数据库这一理念落到了 GraphQL 层只要函数满足VOLATILE、位于内省 schema、不触碰函数限制PostGraphile 就会自动为其生成符合 Relay 规范的 Mutation。掌握STRICT参数必填、SECURITY DEFINER权限提升慎用与LANGUAGE的选择再配合pgStrictFunctions全局策略就能写出既安全又贴合业务的数据库级变更操作。赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐Relay 教程使用 Mutation 与 Updater 实现服务端数据更新Mutations UpdatesRelay 教程使用 Mutation 与 Updater 实现服务端数据更新Mutations Updates 本文是 Relay 官方教程「Mut前端开发工具革命性音乐合成工具audio-diffusion用AI扩散模型创作独特音乐的完整指南 革命性音乐合成工具audio diffusion用AI扩散模型创作独特音乐的完整指南 你是否曾梦想过让AI为你创作音乐audio diffusion正如何快速上手Mockingbird5分钟完成iOS测试环境搭建如何快速上手Mockingbird5分钟完成iOS测试环境搭建 Mockingbird是一款专为Swift和Objective C打造的高效测试框架能帮助开数据库后端上一篇Zewo项目结构全解析从Package.swift到模块化开发下一篇前端交互优化chat.io的客户端JavaScript实现揭秘创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考