PostGraphile V5 定制化完全指南:数据库对象、Smart Tags、Preset 与插件扩展
后端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 提供了 JavaScript 与 SQL 双路径的 API 定制能力你可以从数据库对象表、视图、函数、约束等出发做注解式定制也可以通过配置文件Preset调整全局行为还可以用插件机制深度扩展 GraphQL Schema。本文以官方customization-overview文档为主线结合仓库中 presets 与插件工厂的源码实现系统梳理三大定制工具的用法、内置 Preset 的差异以及常见定制任务的推荐做法读完你即可为项目规划出一条从数据库到 GraphQL 的完整定制路线。三大定制工具先理解整体框架PostGraphile 的核心工作是从数据库自动推导出 GraphQL API因此首要前提是数据库中要有合适的对象表、列、类型、约束、函数、视图、索引等。PostGraphile 能适配多种形态的数据库但官方明确建议不要回避数据库本身强大的特性。在数据库对象之上官方给出了三个层次的定制工具工具作用层次典型入口Annotations注解数据库对象 → 生成结果数据库 “smart comments”、postgraphile.tags.json5Configuration配置全局行为graphile.config.ts中的 PresetExtension扩展Schema 结构与执行逻辑插件尤其是extendSchema插件工厂:::tip 如果你要从 JavaScript/TypeScript 添加字段或类型第一站是学习extendSchema——它是官方推荐的“Schema 加法”插件工厂。 :::数据库对象定制的第一层PostGraphile 鼓励你把精力放在数据库设计上它会尽力从设计良好的数据库 Schema 中自动提取最佳实践的 GraphQL API。设计时请务必遵循最佳实践主键、唯一约束、外键约束、索引等一个都不能少。当你向数据库新增实体且相关权限已GRANT时PostGraphile 会自动生成对应的字段与类型——这也是为什么很多定制“什么都不用做”就能生效。表、视图、物化视图与约束对于数据库中找到的表、视图和物化视图PostGraphile 会自动构建以下内容用于获取全部行的根 Query 字段通过主键和唯一约束获取单行的根 Query 字段反映外键约束的、位于引用方与被引用方类型上的关系字段用于创建、更新、删除这些记录的 Mutation 字段。具体生成哪些内容取决于数据库中的权限GRANT表上定义的约束索引主要影响反向关系配置项与全局默认行为例如默认倾向使用 connections默认还是 lists。这些默认决策可以通过 behavior 系统在全局、单表或单约束粒度上覆盖例如在相关实体上加 smart tags或者设置preset.schema.defaultBehavior。:::tip 给视图添加“虚拟约束” 视图和物化视图原生不支持约束因此 PostGraphile 提供了“虚拟约束”机制你可以通过primaryKey、unique、foreignKey等 smart tags 让视图表现得像拥有主键、唯一约束和外键约束一样。 :::想深入了解表与视图的用法可阅读 tables 和 views外键约束如何形成类型间的 relations 关系也有专门文档说明。函数从易失性推导暴露方式向数据库添加函数时PostGraphile 默认用启发式规则决定它在 Schema 中的暴露方式若函数是volatileCREATE FUNCTION的默认值PostGraphile 假定它会改变数据将其作为mutation暴露——即“custom mutation”函数若函数声明为STABLE或IMMUTABLE则会被加入 query 操作要么作为顶层字段“custom query”函数要么在满足特定条件时成为表类型上的字段即“computed column”函数。:::info “Computed column”函数其实名不副实 “Computed column”函数最初的设计意图是让类型基于自身其他属性新增标量字段但后来功能远超于此如今它甚至可以返回整组相关记录……只是“computed column”这个名字一直沿用目前尚未出现更好的替代最接近的叫法是“custom field”。请记住computed column 函数并不局限于你想象中“列”那样的简单标量。 :::三种函数的深入用法分别见 “custom query” 函数、“computed column” 函数 和 “custom mutation” 函数。Annotations用注解改写生成结果Annotations 分为两类Descriptions描述会写入生成的 GraphQL 类型/字段/参数等作为文档你可以在 GraphiQL 等工具里直接读到Smart tags智能标签影响 PostGraphile 以及你所用插件如何处理被注解的数据库对象。Smart tag 的约定形态是一个名字按惯例以为前缀 一个值值可以是布尔true、字符串或它们的列表。几个高频示例name重命名任意函数、表或视图omit从 API 中隐藏任意字段resultFieldName重命名 mutation 的结果字段视图无法声明约束可以通过foreignKey、primaryKey、unique添加类似约束的字段使其像功能更完整的表一样工作。Annotations 的来源可以组合使用PostgreSQL 注释、JSON tags 文件、插件。Smart comment 语法与解析规则Smart comments 是 smart tags 最古老的载体通过 PostgreSQL 的COMMENT语句写入。解析规则见 smart-comments 文档非常严格一个 smart comment 由若干 tag 和紧随其后的剩余注释组成开头空格和 tab 被忽略首行为空行也会被忽略tag 可以有字符串负载跟在 tag 后、以空格分隔不能包含换行tag 之间用换行\n或\r\n分隔tag 必须以开头且必须位于剩余注释之前无负载的 tag 值为布尔true有负载则为字符串同一 tag 出现多次时最终值为各值的数组。例如下面这段注释name meta isImportant jsonField date timestamp jsonField name text jsonField episode enum ONE1 TWO2 This field has a load of arbitrary tags.会解析出如下 tags 对象{ name: meta, isImportant: true, jsonField: [date timestamp, name text, episode enum ONE1 TWO2] }而最后一行This field has a load of arbitrary tags.则作为描述文档保留。官方推荐用美元符引号dollar quoting书写多行注释注意连续两个换行会把 smart tags 部分与描述正文分隔开两个换行之后的内容即使以开头也不会再被解析为 tag。添加文档描述你可以通过注解修改 GraphQL Schema 中类型或字段的描述。描述会显示在 GraphiQL 的文档面板中也可能出现在编辑器和其它位置。默认情况下PostGraphile 会从每条数据库COMMENT中提取全部 smart tags并把剩余部分用作资源描述COMMENT ON TABLE users IS $$ name people Represents the people that can log in to our application. $$;描述也可以通过postgraphile.tags.json5文件或插件提供。详见 “smart comments” 与 postgraphile.tags.json5。Smart tags 的内置集合Smart tags 不只这三个仓库文档 smart-tags 收录了内置标签的非穷尽列表这里摘录最常用的name作用于表、视图、物化视图、复合类型、列、类型、custom query 函数Query 字段名、custom mutation 函数Mutation 字段名fieldName作用于外键约束本地关系字段名参见foreignFieldName、唯一约束根 finder 字段名、computed column 函数生成的字段名foreignFieldName/foreignSimpleFieldName/foreignConnectionFieldName作用于外键约束在远端类型上的“反向”关系字段名其中后两者分别精确覆盖 list 字段与 connection 字段的命名该名字会按情况送入connectionField默认什么都不做或listField默认追加Listinflectordeprecated作用于列可将列标记为废弃需要多行文本时可重复指定该 tagreturnType作用于 custom query / custom mutation / computed column 函数指定表示函数结果的具名 GraphQL 类型可与 list/connection/non-null 等包装组合若函数返回多态类型命名类型必须是该多态类型本身或其某个实现官方提醒你需自行保证结果与该类型一致resultFieldName作用于 custom mutation 函数指定 mutation payload 类型上的字段名behavior覆盖表、视图、物化视图、类型、列、约束、函数等实体的 behavior见下文arg0variant、arg1variant……作用于 custom query / custom mutation / computed column 函数将复合类型参数转换为patch等价于update*mutation 的参数、nodeId接受类型的全局对象标识或base所有列都可用且可空等“变体”类型argN中的 N 是 0 起始的参数序号notNull将列标记为非空视图列常用primaryKey、unique、foreignKey虚拟约束让视图等无法声明约束的类型具备主键/唯一/外键行为声明主键的列会自动被标记为notNull官方提醒声明的主键必须真的唯一系统不会校验ref/refVia见 Refs。json5 文件中的写法等价于 SQL 注释例如name的两种写法{ version: 1, config: { class: { post: { tags: { name: message, }, }, }, procedure: { search_posts: { tags: { name: returnPostsMatching, }, }, }, }, }comment on table post is Ename message; comment on function search_posts(text) is Ename returnPostsMatching;Smart tags 的注入途径还有pgSmartTags实例以及自定义插件实现gather.hooks.pgIntrospection_introspection回调拿到实体后调用entity.getTagsAndDescription()再修改返回对象的.tags属性。Behavior 系统注解与配置的桥梁New to V5 的 behavior 系统 为“哪些东西暴露、以何种方式暴露”提供了细粒度控制。它的核心是“behavior string”例如insertlist -connection -list:filter-insert -update -delete query:*:filter connection -list每个行为字符串由空格分隔的“行为片段”组成片段可选/-前缀省略时视为后跟由冒号连接的“scope 短语”camelCase 单词或*。最终行为由多个来源拼接而成优先级从低到高大致为插件默认行为 → 全局默认行为 → 插件推断行为 →次要实体行为如列的 codec 行为→ 实体行为smart tags/smart comments。判定时系统从后向前扫描行为字符串第一个匹配片段的-修饰符会否决该行为。用behavior注解即可覆盖实体的默认行为comment on table users is Ebehavior -insert -delete;全局默认行为则由preset.schema.defaultBehavior设置详见下文配置部分。仓库还提供了npx graphile behavior debug命令可快速检查具体实体上哪些行为片段生效及原因。Configuration用 Preset 配置全局行为PostGraphile 高度可配置起点是 PostGraphile 配置文件通常存放在graphile.config.ts也支持.js、.mts、.cjs等。这个文件定义你的配置“preset”可以继承其它 preset、添加插件并为这些插件和 preset 设置配置项。文件中可用的配置项取决于它使用的插件和 preset——这正是官方建议使用 TypeScript 或graphile config options命令来探索可用选项的原因该命令会尝试用 TypeScript language server 判断当前配置下可用的选项。常用选项的快速参考见 config 文档例如pgServices用于声明要连接的 PostgreSQL 数据库name与adaptor为必填schemas、pgSettings、pgSubscriber等可选日常场景优先用makePgService()辅助函数构建而不是手写adaptorSettings。Presets可共享的配置单元Preset 把其它 preset、插件和配置项组合成一个便于共享的 JS 对象。PostGraphile 提供了若干内置 preset源码见 postgraphile/postgraphile/src/presetspostgraphile/presets/amber基础 preset包含 PostGraphile 的大部分功能是必选底座。从源码 amber.ts 可以看到它通过extends继承graphileBuildPreset与graphileBuildPgPreset并以orderedPlugins把PgTablesPlugin、PgRelationsPlugin、PgMutationCreatePlugin、PgMutationUpdateDeletePlugin、PgRowByUniquePlugin等插件排成与 PostGraphile V4 更兼容的顺序最后追加SwallowErrorsPluginpostgraphile/presets/v4帮助从 PostGraphile V4 迁移到 V5。源码 v4.ts 显示它通过makeV4Preset(options)把 V4 的配置项simpleCollections、classicIds、ignoreIndexes、disableDefaultMutations、subscriptions、graphqlRoute等翻译为 V5 等价物并附带PgV4BehaviorPlugin、PgV4InflectionPlugin、PgV4SmartTagsPlugin等插件让 V5 的行为更接近 V4例如把id重命名为rowId的规则反转、nodeId字段名等postgraphile/presets/relay面向希望得到纯正 Relay 风格 Schema 的用户。源码 relay.ts 中的PgRelayPlugin通过globalBehavior关闭主键直接暴露-constraint:resource:update/-constraint:resource:delete改用nodeId:resource:update/nodeId:resource:delete让系统尽可能使用 GraphQL 全局对象标识并把nodeIdFieldName恢复为idpostgraphile/presets/minify面向 Schema 导出工作流特别是 serverless的实验性 preset。源码 minify.ts 表明它由MinifySchemaPlugin与PgRegistryReductionPlugin组成会破坏性地剥离 Schema 描述/废弃标记和 registry 元数据以减小导出体积。如前所述你的graphile.config.ts或类似文件本身也定义了一个 preset// graphile.config.mjs const preset { // 继承官方 preset extends: [postgraphile/presets/amber], // 添加插件 plugins: [], // 配置项 schema: { defaultBehavior: -connection list, }, }; export default preset;注意defaultBehavior是面向最终配置的全局默认值如果你在编写一个会被用户继续覆盖的 preset官方建议改用带schema.globalBehavior的插件字符串会被前置回调则返回行为字符串数组、通常把你新增的行为放在当前传入行为之前这样用户的defaultBehavior优先级更高。仓库文档 behavior.md 给出了完整的FavourListsPlugin示例。Extension用插件扩展 Schema 与执行逻辑PostGraphile 的基本构件是插件一个简单的 JavaScript 对象带有名字并可以挂接到 PostGraphile、Grafast、Grafserv 等项目的各个生命周期。插件极其强大——事实上 PostGraphile 几乎全部核心功能都是通过插件实现的围绕它也形成了丰富的第三方插件生态。插件工厂快速达成目标一些插件如 inflection 插件直接编写即可但涉及扩展和增强 GraphQL Schema 时官方建议使用插件工厂来抽象样板代码extendSchema添加字段和类型的首选工厂。允许你用 GraphQL SDL 描述类型如extend type Query { random: Int }并用 Grafastplan甚至传统 GraphQL resolver提供执行逻辑。从 extend-schema 文档 可见其签名回调接收build对象含sql、inflection、pgRegistry、pgResources等返回含typeDefs以及plans/resolvers的对象官方明确推荐使用plans而非resolversV5 中 resolver 不再获得 Graphile Build 相关的第四个参数lookahead 已被 Grafast查询计划取代。例如import { extendSchema } from postgraphile/utils; import { constant } from postgraphile/grafast; export const MyPlugin extendSchema((build) { return { typeDefs: /* GraphQL */ extend type Query { meaningOfLife: Int } , objects: { Query: { plans: { meaningOfLife() { return constant(42); }, }, }, }, }; });在 plan resolver 里你可以用context().get(userId)读取 GraphQL context、用build.pgResources获取资源、用users.find()/users.get({ id: $userId })获取表示行集合/单行的 step再用$row.get(column)读取列值查询数据库之外的任意逻辑则可借助loadOneWithPgClient/loadManyWithPgClient需要显式传入 executor通常取自channels.executor或build.pgExecutor/build.input.pgRegistry.pgExecutors.main。返回 connection 字段时plan resolver 必须产出connection(...)step否则会报$connection.getSubplan is not a function。wrapPlans增强 PostGraphile 已生成的行为例如在 mutation 完成后执行某动作或自动过滤记录集的一部分结果changeNullability修正 Schema 中字段的可空性标记为 nullable 或 non-nullable详见 change-nullability 文档。如果这些工厂都不满足需求你还可以阅读 extending 文档 学习如何编写自己的 Schema 插件。通用指导何时用哪种方式存储数据优先建表如果需要存储数据第一选择通常是表。只要权限正确添加表会自动在相关位置生成字段再配合外键约束、唯一/主键约束、额外列、函数、插件等方式获得更多字段。派生数据视图、函数还是 Schema 扩展如果是从已有数据派生数据你有三个选择视图、数据库函数、Schema 扩展。总体按个人偏好选择即可但需注意视图不能接受参数且需要注解才能表现得更像表仅在确实需要表式行为时使用例如为底层可变数据库资源构建无版本外观的门面函数可以接受参数但实现不佳时会有显著的性能开销务必熟悉 SQL 函数的 inlining并优先用LANGUAGE sql编写IF、LOOP等过程式结构应尽量让位于声明式 SQL。函数也不能被INSERT/UPDATE/DELETE但你可以再暴露执行这些操作的附加函数Schema 扩展比视图和函数更强大对性能有更强控制例如强制内联甚至把计算搬到 JS 而非数据库但需要熟悉 JS、SQL 与 Grafast规划系统而且因为它们不在数据库中只能被 GraphQL 的消费者使用。函数 vs 插件对于简单标量例如把first_name和last_name拼接成fullName用数据库函数通常更合适。其它情况建议从你最熟悉的方式入手一旦遇到性能问题或发现所需功能与当前模式不兼容就切换到插件——并且这种切换应该能在不破坏 Schema 的前提下完成。注意视图和函数支持本身也是通过插件实现的所以它们能做的任何事插件也都能做到。常见任务速查添加一个 Query 根字段创建数据库表、视图或物化视图创建 custom query 函数使用插件例如通过extendSchema。向表类型添加字段表类型指表示数据库表、视图、物化视图甚至复合类型的 GraphQL 类型部分建议只适用于其中某些子集。添加列来存储数据添加外键约束视图则用foreignKey虚拟约束以建立类型间关系创建 computed column 函数可返回标量、记录甚至记录集合使用插件例如通过extendSchema。向函数派生类型添加字段官方一般不建议在数据库中使用“匿名”类型——函数最好returns setof named_type而不是returns table(...)——但如果你确实用了匿名类型为其添加字段可以修改函数以返回更多值使用插件例如通过extendSchema。添加 Mutation 字段新增表让 CRUD mutation 来修改其数据添加 custom mutation 函数使用插件例如通过extendSchema。重命名字段重命名任意字段有三条路重命名其对应的数据库对象、用namesmart tag 注解、或使用 inflection 插件。移除字段一般来说预防字段被添加比添加后再移除更好。要避免字段进入 Schema可以从数据库删除对应对象、撤销其上的权限或添加behavior -*smart tag也可以只移除特定行为例如behavior -update。如果这些都不奏效还可以从 Schema 中显式删除字段。添加文档数据库表、视图、物化视图、约束等可以用COMMENTSQL 命令添加注释。注意每次对同一实体执行该命令都会覆盖之前的注释需小心操作用 smart tags 系统从文件或插件加载描述应用到数据库派生的 GraphQL 实体上用插件设置描述大多数实体都有description字段可以通过 graphile-build 插件钩子覆盖——当你需要跨 Schema 设置大量相似描述时这种方式尤其有用。小结一条可落地的定制路线综合以上内容PostGraphile V5 的定制思路可以总结为“数据库优先、注解其次、配置兜底、插件兜顶”先把数据库对象设计好主键、唯一/外键约束、索引、视图与函数让 PostGraphile 自动推导出尽可能接近目标的 Schema用 smart comments /postgraphile.tags.json5做细粒度注解重命名、隐藏、虚拟约束、行为覆盖在graphile.config.ts中通过 Preset 组合全局行为选择amber底座按需叠加v4、relay、minify预设遇到数据库与注解都搞不定或性能不佳的场景用extendSchema、wrapPlans、changeNullability等插件工厂在 Schema 层精确控制实现从简单标量到复杂事务 mutation 的任意扩展。这套组合拳覆盖了从数据库建模到 GraphQL 暴露的完整链路也是官方文档与仓库源码presets、smart-tags、behavior、extend-schema共同描绘的标准实践。赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐PostGraphile 定制化总览从数据库对象、Smart Tags 到 Presets 与插件扩展PostGraphile 定制化总览从数据库对象、Smart Tags 到 Presets 与插件扩展 PostGraphilev5即 Grafast 时后端API网关PostGraphile Smart Tags 完全指南用数据库注释与 JSON5 配置定制 GraphQL SchemaPostGraphile Smart Tags 完全指南用数据库注释与 JSON5 配置定制 GraphQL Schema 本文基于 PostGraphile后端API网关PostGraphile Smart Tags 完全指南不修改数据库即可深度定制 GraphQL SchemaPostGraphile Smart Tags 完全指南不修改数据库即可深度定制 GraphQL Schema 本篇指南围绕 PostGraphile 的 S后端API网关创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考