- 后端
- API网关
【免费下载链接】crystal
🔮 Graphile's Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!
本文是一份聚焦 PostGraphile v5(本仓库postgraphile/postgraphile)自动生成 CRUD Mutations 的技术指南,涵盖其生成规则、behavior禁用方式、字段命名约定与完整 GraphQL 实战示例,并深入@dataplan/pg源码讲解 Insert / Update / Delete 步骤的底层 SQL 生成原理。读完本文,你将掌握 PostGraphile 中 CRUD Mutations 的开关控制、权限联动规则,以及 mutation 不出现时的系统化排查方法。
CRUD(Create / Read / Update / Delete,即"增删改查")是数据操作 API 中最常见的范式;所谓"CRUD Mutations",指的是其中除 "R"(Read)之外的全部写操作。PostGraphile 会自动为拥有相应数据库权限的每一张表,在生成的 GraphQL Schema 的根Mutation类型上添加对应的 CRUD Mutations。本文档对应仓库文件:crud-mutations.md。
设计 Mutations:按需关闭 CRUD 自动生成
PostGraphile 的自动化并不意味着你必须接受默认的一切。如果你希望所有 mutation 都由自己定义(例如通过自定义 Mutations),可以很容易地在 preset 中通过禁用insert、update、delete三个 behavior 来关闭 CRUD Mutations 的自动生成:
export default { // ... schema: { defaultBehavior: "-insert -update -delete", }, };在 PostGraphile 中,defaultBehavior属于schema配置块,其值是一个用空格分隔的 behavior 列表;-前缀表示移除该行为。上述配置等价于告诉 PostGraphile:"所有表都不再自动生成插入、更新、删除类 mutation"。behavior 系统是 PostGraphile 的核心机制之一,行为既可以像这样在全局统一设置,也可以针对单张表通过 smart comments(如@behavior -insert -update -delete)进行局部覆盖,详见 behavior 文档与 smart tags 文档。
一个常见的认知误区
一个对 PostGraphile 不熟悉的开发者常见的误解是:PostGraphile 的核心功能就是 CRUD Mutations。实际上,相当大比例的用户(包括维护者本人)几乎不使用 CRUD Mutations。PostGraphile 鼓励你写出尽可能好的 GraphQL API,因此在设计自己的 mutation 之前,官方强烈建议阅读 Marc-André Giroux 的经典文章GraphQL Mutation Design: Anemic Mutations,理解"贫血型 mutation"的设计理念。
PostGraphile 提供了多种自定义 mutation 的途径,你可以按团队最舒服的模式来选:
- 数据库函数(database functions):在 PostgreSQL 中编写业务逻辑函数,自动暴露为 mutation;
- Schema 扩展(schema extensions):用 SDL 扩展 Schema;
- 自定义插件(custom plugins):通过插件系统深度定制。
注意:PostGraphile 的价值远不止 CRUD Mutations
你可能会问:"如果不用 CRUD Mutations,用户还能从 PostGraphile 中获得什么价值?"这些用户通常看重的是 PostGraphile 在查询 Schema 上带来的显著效率提升——这意味着他们可以支撑更大规模的流量,并且在更长时间内无需为缓存和缓存失效的复杂性操心。此外还有自动生成带来的一致性与时间节省、开箱即用的 Schema 所遵循的 GraphQL 最佳实践,以及通过插件与 behavior 系统实现的轻松的全 Schema 级变更。这些都是 PostGraphile 广为人知的核心特性。
CRUD Mutation 字段一览
以父文章《PostgreSQL Tables》中的users表为例:
create table app_public.users ( id serial primary key, username citext not null unique, name text not null, about text, organization_id int not null references app_public.organizations on delete cascade, is_admin boolean not null default false, created_at timestamptz not null default now(), updated_at timestamptz not null default now() );根据你的 PostGraphile 设置(以及你授予的数据库权限),你可能会得到以下 mutations:
| Mutation 字段 | 说明 |
|---|---|
createUser | 创建单个User。参见示例 |
updateUser | 使用全局唯一 ID 与 patch 更新单个User |
updateUserById | 使用唯一键与 patch 更新单个User。参见示例 |
updateUserByUsername | 使用唯一键与 patch 更新单个User |
deleteUser | 使用全局唯一 ID 删除单个User |
deleteUserById | 使用唯一键删除单个User。参见示例 |
deleteUserByUsername | 使用唯一键删除单个User |
关键规则:
update与deletemutations 只有在表包含primary key列时才会被创建。
作为对照,同一张表还会生成如下"Read"侧的查询字段:
user—— 使用全局唯一ID返回单个User;userById—— 使用全局唯一ID读取单个User;userByUsername—— 使用唯一username读取单个User;allUsers—— 返回一个支持分页的 connection。
字段的命名遵循 PostGraphile 的 inflector 规则:mutation 名由create/update/delete+ 类型名(UpperCamelCase)构成;当存在多个唯一约束时,会生成按唯一键后缀区分的变体(如ById、ByUsername)。可以注意到users表同时拥有id主键和username唯一约束,因此自动生成了两套按键定位的字段。
实战示例:Create / Update / Delete
Create:创建记录
# Create a User and get back details of the record we created mutation { createUser( input: { user: { id: 1, name: "Bilbo Baggins", username: "bilbo" } } ) { user { id name username createdAt } } }createUser接受一个input参数,其中user对象承载待插入的列值。PostGraphile 会在执行后通过RETURNING把所选的字段(如createdAt)返回给客户端——这正是"返回创建后的记录"的实现基础。
Update:更新记录
# Update Bilbo using the user.id primary key mutation { updateUserById( input: { id: 1, userPatch: { about: "An adventurous hobbit" } } ) { user { id name username about createdAt } } }更新类 mutation 由两个部分组成:用于定位记录的唯一键(如id)和用于描述变更的userPatch对象。patch 对象中只包含可选的列字段,仅提交其中出现的列会被更新,未提及的列保持不变。
Delete:删除记录
# Delete Bilbo using the unique user.username column and return the mutation ID mutation { deleteUserByUsername(input: { username: "bilbo" }) { deletedUserId } }删除类 mutation 同样可以按主键或任意唯一键定位记录,并返回如deletedUserId这样的删除结果字段,便于客户端确认被删除的行。
底层原理:@dataplan/pg 的 Insert / Update / Delete 步骤
PostGraphile v5 的 CRUD Mutations 最终落在@dataplan/pg的三个核心步骤类上,它们分别对应 SQL 的INSERT、UPDATE与DELETE语句生成:
PgInsertSingleStep—— 向资源(表)插入一行。它的set(name, value)方法记录待插入的属性与依赖,execute()中把属性拼接为insert into ${table} (${attributes}) values (${values}) returning ...语句;当没有提供任何列时则退化为insert into ... default values(见 pgInsertSingle.ts#L377-L387)。PgUpdateSingleStep—— 通过getBy参数定位单行并更新。从源码结构看,它同时维护getBys(定位条件)与attributes(待更新列)两套依赖,并在finalize阶段生成带WHERE条件的 UPDATE 语句。PgDeleteSingleStep—— 删除一行并可以返回被删行的列。它与 Update 步骤类似,通过唯一键构建定位条件,删除后返回选中列供 mutation 结果使用。
这几个步骤类均设置了isSyncAndSafe = false并声明hasSideEffects = true,明确告知 Grafast 执行引擎这些计划不可并行安全缓存、必须真实提交到数据库——这正是 mutation 与查询步骤的本质区别。它们还都实现了selectAndReturnIndex/get机制:mutation 结果中需要返回哪些列(如createdAt、about),会以RETURNING子句的形式附加到 SQL 中,从而在一次数据库往返内完成写入与读取。
需要说明的是,PgInsertSingleStep的源码注释指出:尽管批量插入(bulk insert)看起来更高效,但由于依赖自增主键、触发器改写数据等场景无法可靠地把结果行与输入一一对应,PostgreSQL 官方也不保证ORDER BY顺序,因此当前实现采用单行插入策略,但多个 mutation 可以并行执行。
权限:CRUD Mutations 的"闸门"
如果你使用PgRBACPlugin(在不使用makeV4Preset()时默认启用),PostGraphile 只会暴露你真正有权限访问的表 / 列 / 字段。例如执行了:
GRANT UPDATE (username, name) ON users TO graphql_visitor;那么updateUsermutations 就只接受username和name两个字段——其余列不会出现在 Schema 中。
PgRBACPlugin会检查数据库中的 RBAC(GRANT/REVOKE)权限并将其反映到 GraphQL Schema 中。遵循 GraphQL 最佳实践,它仍然只生成一个GraphQL Schema(而非每个用户一个),其做法是:从连接字符串使用的 PostgreSQL 账号出发,遍历该用户在数据库中能够切换(become)的所有角色,取所有这些权限的并集。你可以通过pgService.pgSettingsForIntrospection对象影响其使用的设置。官方推荐使用该插件,因为它能让 Schema 更精简,不包含你实际上无法使用的功能。
官方强烈建议不要对 PostGraphile 使用基于列的
SELECT授权(见 requirements.md)。更好的做法是把权限关注点拆分到独立的表中,再通过一对一关系关联。
排查清单:如果 Mutations 没有出现……
首先,检查你的 PostGraphile 服务器是否有错误输出。如果没有错误,那么 mutations 未出现在生成的 Schema 中,通常可以按以下原因排查:
- 行为(behavior)被禁用:例如配置了
defaultBehavior: "-insert -update -delete",或在表上打了@behavior -insert -update -delete这类 smart comments; - 表权限不足:数据库账号缺少对应的 INSERT / UPDATE / DELETE 权限;
- 表不在被暴露的 schema 中:PostGraphile 只处理你指定的 schema(如
app_public); - 视图(views)而非表:默认情况下视图不会自动获得 CRUD Mutations;
- 缺少主键:
update与delete需要主键;不过即便没有主键,createmutations 仍然会被添加; - 只看到基于主键的 mutation:你可能正在使用
PrimaryKeyMutationsOnlyPlugin,该插件会把按键定位的 mutation 限制为只使用主键。
另外,如果你刚接触 GraphQL,也许只是找错了地方:在 Ruru(Graph*i*QL 界面)中打开右侧的文档并回到根节点,选择Mutation类型即可看到可用的 mutations。尝试执行 mutation(例如使用自动补全)时,必须在组合请求时使用mutation操作类型:
mutation { createThing... }否则 GraphQL 会默认把请求解释为query,自然找不到 mutation 字段。
总结
PostGraphile v5 的 CRUD Mutations 是"权限驱动、行为可控"的自动生成机制:它由数据库表结构与 RBAC 权限推导 Schema,又通过behavior系统提供全局或逐表的精细化开关;生成的字段命名遵循 inflector 规则并按唯一键派生变体,底层则由@dataplan/pg的PgInsertSingleStep/PgUpdateSingleStep/PgDeleteSingleStep编译为真实的 SQL 语句。无论你选择直接使用这些自动化 mutation,还是关闭它们并用数据库函数、Schema 扩展或自定义插件完全接管写操作,理解本文的生成规则与排查路径都能让你对最终 Schema 的形态拥有确定的预期。
- 后端
- API网关
【免费下载链接】crystal
🔮 Graphile's Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!
相关推荐
PostGraphile CRUD Mutations 完全指南:自动增删改查的生成机制、行为控制与故障排查
PostGraphile CRUD Mutations 完全指南:自动增删改查的生成机制、行为控制与故障排查 CRUD(Create、Read、Update、D
后端API网关PostGraphile CRUD Mutations 完全指南:自动生成的增删改操作、字段规则与故障排查
PostGraphile CRUD Mutations 完全指南:自动生成的增删改操作、字段规则与故障排查 PostGraphile 会根据数据库中的表自动生成
后端API网关如何快速下载B站字幕:3步实现视频学习自由
如何快速下载B站字幕:3步实现视频学习自由 还在为B站视频字幕无法保存而烦恼吗?BiliBiliCCSubtitle是一个专门为B站用户设计的开源工具,让你能够
Web框架后端前端CLI开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考