ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PostGraphile processSchema 插件全指南:在 Schema 构建完成后注入自定义处理逻辑

PostGraphile processSchema 插件全指南:在 Schema 构建完成后注入自定义处理逻辑 PostGraphile processSchema 插件全指南在 Schema 构建完成后注入自定义处理逻辑【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal导读processSchema是 PostGraphile 提供的一个内置工具插件它让你在 GraphQL Schema 构建完成之后、投入服务之前插入一段自定义处理逻辑。无论是把 SDL 打印到文件、将可执行 Schema 导出为 JavaScript 代码、校验自定义业务规则还是把 Schema 替换为 Mock 或衍生版本都可以通过这个插件统一完成。读完本文你将掌握processSchema的完整签名、底层实现原理finalizehook、同步回调的约束以及一个可直接运行的exportSchema实战示例。什么是 processSchema根据官方文档 process-schema.md 的定义这是一个在 Schema 构建后对其进行处理processing the schema after its built的插件。它的典型使用场景包括将 Schema 的 SDL 打印到文件将 Schema 的 SDL 上传到网络服务例如注册到 Schema Registry用 Schema 与你的持久化查询persisted queries清单做交叉校验用 Schema 验证你的自定义业务逻辑将可执行 SchemaJavaScript 形式导出到文件用 Mock 版本或衍生版本替换原 Schema例如与其他 Schema 做 stitching与第三方库集成。可以看到processSchema并不绑定某一个具体功能而是一个通用的Schema 后处理挂载点凡是需要在 Schema 生成之后、对外提供之前执行的逻辑都可以收敛到这里。函数签名与底层实现官方文档给出的签名如下function processSchema( process: (schema: GraphQLSchema) GraphQLSchema, ): GraphileConfig.Plugin;它只接受一个参数一个 schema 处理函数。该函数会被调用并传入构建完成的 GraphQL Schema且必须满足返回同一个Schema适用于只读操作或直接对 Schema 原地修改返回另一个Schema通常是原 Schema 的衍生版本如 Mock、stitching 后的结果。从源码看实现原理在 monorepo 中processSchema的实现位于 makeProcessSchemaPlugin.ts完整源码如下import type { GraphQLSchema } from grafast/graphql; import type {} from graphile-config; let counter 0; type ProcessSchemaFunction (schema: GraphQLSchema) GraphQLSchema; export function processSchema( callback: ProcessSchemaFunction, ): GraphileConfig.Plugin { return { name: ProcessSchemaPlugin_${counter}, version: 0.0.0, schema: { hooks: { finalize: { callback, }, }, }, }; } /** deprecated use processSchema */ export const makeProcessSchemaPlugin processSchema;从中可以提炼出三个关键实现事实它本质上是一个GraphileConfig.Plugin插件把用户传入的回调注册到了schema.hooks.finalize这个 hook 上。也就是说PostGraphile 构建 Schema 的流程中预留了finalize最终化阶段processSchema就是在该阶段挂载你的处理函数这与Schema 构建完成后处理的语义完全对应。插件名自动生成且唯一每次调用都会通过ProcessSchemaPlugin_${counter}生成一个递增的插件名因此你可以安全地多次调用processSchema而不会与其他插件重名冲突。makeProcessSchemaPlugin是旧名称源码中保留了/** deprecated use processSchema */的makeProcessSchemaPlugin别名导出见 graphile-utils 的入口文件新代码应统一使用processSchema。导出路径官方文档示例从postgraphile/utils导入processSchemaimport { processSchema } from postgraphile/utils;这个路径是有效的PostGraphile 自己的 README.md 中就使用了import { extendSchema } from postgraphile/utils的写法其 CHANGELOG.md 也提到了postgraphile/utils这个导出路径。在 monorepo 内该工具的具体实现位于graphile-build/graphile-utils包中makeProcessSchemaPlugin.ts并通过postgraphile/utils对外转发。回调是同步的异步任务的处理方式文档用一个:::info提示块专门强调processSchema的回调是同步执行的。这带来一个直接后果如果你在回调里发起异步任务比如fs.promises.writeFile、fetch上传等那么这个异步任务的结果不会影响回调的返回值也就不会影响最终被服务器使用的 Schema。因此异步任务的错误必须由你自己捕获并处理例如console.error否则可能产生未处理的 Promise rejection。推荐的异步写法是同步发起、独立追踪回调内部启动异步操作并附带.catch()同时立即同步返回原 Schema。官方示例就是这一模式的体现详见下文。与第三方工具兼容性的重要警告文档用一个:::warning提示块给出了一个重要警告PostGraphile 的 Schema 使用 Gra*fast* 的 plan resolvers计划解析器而不是传统的 GraphQL resolvers。因此任何通过操作传统 resolvers 来工作的第三方工具都很可能破坏 PostGraphile 的 Schema从而无法达到预期目标。文档明确点名的例子是graphql-shield——它目前与 Gra*fast* plans 不兼容。这意味着在使用processSchema做替换 Schema或集成第三方库时必须确认目标库是围绕 GraphQL 类型系统本身SDL、类型定义、指令等工作而不是围绕传统 resolver 字段函数工作。对于需要在执行层面加权限等逻辑的场景应优先考虑 PostGraphile 的原生方案如pgSettings、插件 hook 等而不是依赖传统 resolver 中间件。实战示例导出 Schema 为可执行代码文档给出了一个完整、可直接运行的示例构建完成后把 Schema 导出为可复用的 ES Module 文件。import { processSchema } from postgraphile/utils; import { exportSchema } from graphile-export; const ExportSchemaPlugin processSchema((schema) { exportSchema(schema, ${process.cwd()}/exported-schema.mjs, { mode: typeDefs, }).catch((e) { console.error(e); }); return schema; });逐行解读这个示例processSchema((schema) {...})注册一个后处理回调入参是构建完成的GraphQLSchema。exportSchema(schema, path, { mode: typeDefs })来自graphile-export包monorepo 中位于 utils/graphile-export/src/exportSchema.ts。它把 Schema 序列化并写入指定路径的.mjs文件。mode: typeDefs表示只导出类型定义SDL 风格的可执行模块而非带完整计划的 Schema 实例。.catch((e) { console.error(e); })因为exportSchema返回 Promise 而回调是同步的这里必须显式捕获错误防止未处理的 rejection即便写文件失败服务器仍会继续使用原 Schema。return schema返回同一个 Schema表示本次操作是只读/旁路式的不改动服务器实际使用的 Schema。exportSchema 的底层行为从graphile-export的源码exportSchema.ts可以看到exportSchema是一个async函数export async function exportSchema( schema: GraphQLSchema, toPath: string | URL, options: ExportOptions {}, ): Promisevoid { const { code } await exportSchemaAsString(schema, options); const toFormat HEADER code; const formatted await format(toFormat, toPath, options); await writeFile(toPath, formatted); await lint(formatted, toPath); }它内部依次完成通过exportSchemaAsString把 Schema 转换为代码字符串支持mode: typeDefs等模式在文件头部追加一段 eslint-disable 注释HEADER常量避免导出文件被仓库的graphile-export/*ESLint 规则误报使用 Prettier 格式化代码后写入目标路径toPath可以是文件路径或URL最后对生成文件做一次 lint 校验。因此这个导出文件是格式化、可 lint、可直接 import 复用的完整 ES Module适合提交到仓库、用于 schema diff 或作为下游工具如代码生成器的输入。更多实战场景怎么用、用在哪基于文档列出的 use cases下面是几种典型的落地方式。1. 打印 / 导出 SDL 到文件不需要第三方库直接用graphql的printSchemaimport { printSchema } from graphql; import { processSchema } from postgraphile/utils; const PrintSchemaPlugin processSchema((schema) { // 同步写文件或用同步启动 catch 的异步写法 require(fs).writeFileSync(schema.graphql, printSchema(schema)); return schema; });适合 CI 中对比 Schema 快照、或在发布前把 SDL 上传到 Schema Registry 的场景。2. 校验 Schema 是否符合自定义逻辑import { processSchema } from postgraphile/utils; const ValidateSchemaPlugin processSchema((schema) { const queryType schema.getQueryType(); if (!queryType || queryType.getFields().some((f) f.name.startsWith(_))) { throw new Error(Schema validation failed: illegal internal fields!); } return schema; });注意这里同步抛出异常是允许的因为回调本身是同步的异常会沿着插件 hook 的执行链向上传播从而中断 Schema 构建——这正是验证自定义逻辑类需求的正确用法区别于旁路式异步任务。3. 用 Mock 或衍生 Schema 替换import { processSchema } from postgraphile/utils; const MockSchemaPlugin processSchema((schema) { // 假设 mockSchema 由你的 mock 库基于原 schema 构建 return mockSchemaFrom(schema); });此时返回的不是原 Schema而是替代 Schema服务器将使用替代结果对外服务。同理Schema stitching 等衍生场景也通过返回新 Schema实现。4. 与持久化查询交叉检查在回调中加载你的 persisted queries 清单用schema.getQueryType()等方法逐条解析查询语句检查是否有字段已不存在——失败时同步抛错即可在启动阶段快速暴露 Schema 变更带来的破坏性影响。如何把插件接入 PostGraphileprocessSchema的返回值是一个GraphileConfig.Plugin因此它和其他插件一样通过 preset 的plugins数组注册。以graphile.config.ts为例import type {} from postgraphile; import { processSchema } from postgraphile/utils; import { exportSchema } from graphile-export; const ExportSchemaPlugin processSchema((schema) { exportSchema(schema, ${process.cwd()}/exported-schema.mjs, { mode: typeDefs, }).catch((e) console.error(e)); return schema; }); export default { schema: { plugins: [ExportSchemaPlugin], }, };也可以在编程方式构建 PostGraphile 时把插件加入graphileOptions/ preset 的 plugins 数组。由于每个processSchema(...)调用都会生成唯一插件名ProcessSchemaPlugin_N你可以在同一 preset 中注册多个processSchema插件它们会按插件顺序依次在finalize阶段执行。相关资源文档原文process-schema.mdv5 版本文档见 version-5/process-schema.md内容一致插件实现源码graphile-build/graphile-utils/src/makeProcessSchemaPlugin.ts导出工具源码utils/graphile-export/src/exportSchema.ts扩展 PostGraphile 的更多方式extending.mdx小结processSchema是 PostGraphile 提供给开发者的一把后处理钥匙它通过schema.hooks.finalize钩子把回调挂进 Schema 构建流程的末端让你既能做打印、导出、上传、校验这类旁路操作返回原 Schema也能做替换、Mock、stitching 这类衍生态操作返回新 Schema。使用时的两个关键纪律是回调保持同步、异步任务自行捕获错误以及牢记 PostGraphile 基于 Gra*fast* plans 的执行模型避免引入操作传统 resolver 的不兼容第三方库。【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表