十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Graphile Crystal Monorepo 全景指南:从 Gra*fast* 规划执行引擎到 PostGraphile 自动 GraphQL API

Graphile Crystal Monorepo 全景指南:从 Gra*fast* 规划执行引擎到 PostGraphile 自动 GraphQL API Graphile Crystal Monorepo 全景指南从 Grafast规划执行引擎到 PostGraphile 自动 GraphQL API【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystalGraphile Crystal 是 Graphile 团队围绕 GraphQL 技术栈打造的 monorepo 仓库核心承载两大明星项目下一代 GraphQL 规划与执行引擎Gra*fast*以及基于 PostgreSQL 自动生成高性能 GraphQL API 的PostGraphile同时还包含pg-sql2、pg-introspection、graphile-config、graphile-export等可独立使用的基础库。本文以仓库根目录 README.md 为骨架结合各子包源码与文档系统梳理整个技术栈的定位、用法与底层实现帮助读者快速判断哪些包适合引入自己的项目以及如何将它们组合成一条高效的 GraphQL 后端链路。仓库定位Graphile 的 Crystal 全家桶据根目录 README.md 介绍这个仓库收纳了 Graphile 旗下几乎所有与 GraphQL 相关的包——包括与 GraphQL 相关的包、以及与与 GraphQL 相关的包相关的包。两个主角分别是Gra*fast*面向 GraphQL.js 的尖端规划与执行引擎可作为 GraphQL.js 官方execute方法的即插即用替代品。通过把传统 resolver 迁移到 Gra*fast* 的 plan resolver计划解析器可以借助 GraphQL 请求的声明式特性以最高效的方式执行业务逻辑从而降低服务器负载。PostGraphile以 PostgreSQL 数据库为核心数据源自动生成结构良好、高性能的 GraphQL API主打低投入、高性能、自动最佳实践与可扩展性。仓库的工程组织方式是 Yarn Workspaces 多包仓库见 package.json 中workspaces字段工作区覆盖grafast/*、graphile-build/*、postgraphile/*、utils/*四组包。环境要求为node 22、yarn 1.3.2且通过packageManager声明使用yarn4.12.0。提示需要 PostGraphile V4旧版的用户README 指明请前往legacy分支查看本仓库主线对应的是 PostGraphile V5 及配套的新一代技术栈。Gra*fast*面向 GraphQL 的规划与执行引擎核心理念从 resolver 到 plan resolvergrafast/grafast/README.md 详细描述了 Gra*fast* 的工作方式它理解 GraphQL并在你的帮助下理解业务逻辑从而以极高效率编排一次 GraphQL 请求的数据需求。传统 GraphQL.js 的执行方式是为每个字段直接调用 resolver 函数而 Gra*fast* 引入了plan resolver每个字段不再直接返回数据而是描述要执行该字段所需的抽象步骤step。一次请求中所有字段的 step 会被组合成一个operation plan操作计划该计划在执行前可以被整体重写与优化并且常常能被未来相似的查询直接复用——这是 Gra*fast* 效率优势的核心来源。关键的一点是Gra*fast*向后兼容传统 resolver绝大多数现有 GraphQL.js schema 都能直接交给 Gra*fast* 执行且通常能获得小幅提速。只有把 resolver 替换为 plan resolver才能看到真正的效率跃升。此外Gra*fast* schema 本身就是 GraphQL.js schema因此 schema-first、code-first 或自动生成等构建方式全都适用。执行流程当 Gra*fast* 第一次见到某个 GraphQL 请求时会经历如下阶段见 grafast/grafast/README.md规划plan分析请求的数据需求、需要执行的步骤、如何把结果写入响应优化optimize对初稿计划进行重写优化例如删除冗余或重复的处理步骤、重写并合并处理步骤以获得最佳性能执行execute执行计划并把响应返回给客户端复用后续与该计划兼容的请求可以直接执行无需重新规划。从源码结构看规划与执行逻辑分布在 grafast/grafast/src/engine/ 下如executeBucket.ts、executeOutputPlan.ts等而对外导出的execute函数定义在 grafast/grafast/src/execute.ts印证了 README 所述即插即用替代execute的接口设计。使用方式两行代码迁移README 给出了两种迁移方式把从graphql模块导入换成从grafast导入即可-import { graphql } from graphql; import { grafast as graphql } from grafast;-import { execute } from graphql; import { execute } from grafast;任何允许替换execute方法的 GraphQL 服务器包括所有完整支持 Envelop 的服务器都可以接入 Gra*fast*。兼容性要求与建议要在 Gra*fast* 上正常运行schema 需要满足以下条件摘自 grafast/grafast/README.md使用GraphQL.js v16不得覆盖 GraphQL 默认字段 resolver该能力暂未支持每次请求的context必须是一个对象需要能作为WeakMap的键使用如果不需要 context传{}即可仅支持 schema 中显式定义的字段 resolver通过rootValue传入的 resolver 暂不支持对传统 resolver 第四参数resolveInfo的支持未完全对齐尤其是resolveInfo.path属性目前不支持。为了最大化收益README 还给出三条关键实践建议见 grafast/grafast/README.md关键用 LRU 缓存缓存解析后的 GraphQL documentAST使同一文档反复复用同一 AST——grafserv会帮你处理Envelop 用户可用envelop/parser-cache不要使用rootValue改用context尽量对 variables 对象做 memoize例如基于canonicalJSONStringify(variables)做缓存使相同 variables 命中同一内存对象用 LRU 缓存 GraphQLcontext对象让同一用户复用同一 context这条相对次要迁移期仍需使用 DataLoader 时可以放宽。这些建议背后的逻辑是Gra*fast* 的规划结果复用程度越高性能收益越大。配套计划类库Gra*fast* 生态提供了两组针对具体数据源的 plan 类dataplan/pg用于与 PostgreSQL 交互的高度优化 Gra*fast* step 类集合。README 将其定位为 A collection of extremely highly optimized Gra*fast* step classes for interacting with PostgreSQLPostGraphile 的 SQL 数据访问正是建立在它之上。dataplan/json用于 JSON 编解码的 plan 类。两者都以独立 npm 包的形式位于grafast/工作区下可脱离 PostGraphile 单独用于自建 Gra*fast* schema。PostGraphile把 PostgreSQL 变成 GraphQL 事实源工作方式PostGraphile 的定位是只写真正带来价值的代码。开箱即用地分析 PostgreSQL 数据库表、关系、函数、索引、权限以及你的配置生成一个完整、一致的 GraphQL schema并且这个活的基础会随数据库演进。在此基础上可以无缝叠加定制与扩展用自定义类型和字段扩展 schema字段可以执行 SQL 或 Node.js 代码用数据库权限决定暴露哪些部分快速、符合人体工学、粒度精细还能提升安全态势用强大的插件与 preset 系统对生成的 schema 施加通用偏好用简单的 tags智能注释微调单个数据库实体重命名、决定暴露方式、改变类型/呈现/nullability、标注抽象类型interface/union、引入额外关系等用 inflection 系统全局重构生成的命名使用第三方插件扩展能力。PostGraphile 几乎所有功能——从 introspection 到类型生成再到分页参数——都是通过插件实现的插件 API 面向使用者设计并提供帮助工厂helper factories让常见需求更易用。在效率方面PostGraphile 由 Gra*fast* 引擎驱动README 指出其性能通常优于使用传统 GraphQL.js resolver 的手写 schema。同时它无锁定必要时可以通过 graphile-export 把 schema 导出为可执行代码脱离 PostGraphile 自行维护仍保留完整规划的执⾏性能优势。实战示例一用 SQL 函数与智能注释定制字段PostGraphile README 给出了一个电商结账场景数据库只有products、prices、cart_items等底层表而前端需要subtotal、tax等汇总字段。第一种做法是直接在数据库里处理见 postgraphile/postgraphile/README.md-- 从 GraphQL schema 中隐藏 prices 表 comment on table prices is behavior -*; -- 创建 Product.unitPrice 字段获取商品当前价格 create function products_unit_price(p products) returns money as $$ select unit_price from prices where product_id p.product_id and now() valid_from and now() valid_until; $$ language sql stable; -- 为该字段添加文档 comment on function products_unit_price is The unit price at the current time, reflecting promotional discounts.;这段 SQL 会自动生成Product.unitPrice字段按时间有效性取当前单价假设各时间段不重叠若可能重叠可在查询中追加order by unit_price asc limit 1。这正是用智能注释驱动 schema 生成的代表性用法。实战示例二用 extendSchema 接入 Node.js 业务逻辑如果业务逻辑更复杂例如需要查询外部服务计算运费可以在 TypeScript 侧用extendSchema扩展 schema见 postgraphile/postgraphile/README.mdimport { extendSchema } from postgraphile/utils; import { constant, context, get } from postgraphile/grafast; import { batchSummarizeCart } from ./businessLogic/cart; export default extendSchema((build) { const { pgExecutor, pgResources: { cartItems, products, prices }, } build; return { typeDefs: /* GraphQL */ extend type Product { The unit price at the current time, reflecting promotional discounts. unitPrice: Money! } extend type Cart { summary: CartSummary } type CartSummary { subtotal: Money! shipping: Money! tax: Money! total: Money! } , plans: { Product: { unitPrice($item) { // 找到对应的价格记录 const productId $item.get(product_id); const $prices prices.find({ productId: $productId }); $prices.where(sqlnow() valid_from and now() valid_until); // 恰好一行取回并返回单价 return $prices.single().get(unit_price); }, }, Cart: { summary($cart) { const $cartId $cart.get(id); return loadOne($cartId, batchSummarizeCart); }, }, // CartSummary 无需 plan resolver使用默认即可。 }, }; });配套的批量加载业务逻辑来自 postgraphile/postgraphile/README.md展示了如何把 DataLoader 风格的回调接到loadOne/loadMany上并通过shared: () context()让 loader 访问运行时 GraphQL context// businessLogic/cart.ts import { context } from postgraphile/grafast; export const batchSummarizeCart { // 计划步骤在 loader 中获取 GraphQL context shared: () context(), // cartIds 是 Cart 标识符的批shared 是运行时 GraphQL context整批共享 async load(cartIds, { shared }) { const carts await batchGetCartInfo(shared, cartIds); const cartsWithShipping await batchCalculateShippingCosts(carts); const cartsWithTax await batchCalculateTax(cartsWithShipping); return cartIds.map((cartId) { const cartInfo cartsWithTax.find((c) c.cart_id cartId); const { subtotal, shipping, tax } cartInfo; const total subtotal shipping tax; return { subtotal, shipping, tax, total }; }); }, };其中的batchGetCartInfo对整批 cart 只发起一次数据库查询where carts.id any($1::int[])这正是 Gra*fast* 批量执行消除 N1 问题的体现。通用工具与基础设施包除了两大主角README 还罗列了一批可独立使用或支撑上层的基础包graphile-export 与 eslint-plugin-graphile-exportgraphile-export 可以在合适的条件下把内存中动态构建的 GraphQL schema 导出为可直接导入执行的原始 JavaScript 源码——这是 PostGraphile 无锁定能力的技术基础。eslint-plugin-graphile-export 则是配套 ESLint 插件帮助开发者编写与 graphile-export 兼容的代码如ExhaustiveDeps.ts、ExportInstances.ts、ExportMethods.ts、ExportPlans.ts等规则见其src/目录。graphile-config统一配置层graphile-config 处理 Graphile 全家桶的插件、preset 与配置文件是一个通用配置层提供Plugin与Preset别名Config两个接口。Plugin 对象包含namestring插件名必须唯一用于skipPlugins等能力versionstringsemver 兼容版本通常与package.json一致也可不同例如一个模块包含多个插件description可选stringCommonMarkMarkdown格式的人类可读描述provides可选string[]该插件提供的 feature labels主要用于决定插件及其 hooks/events的执行顺序在已加载插件中必须唯一缺省时取插件名after可选string[]声明应在指定 feature若存在之后加载before可选string[]声明应在指定 feature若存在之前加载。Preset 则把一组插件与各 scope 的选项打包可同时使用多个 presetpreset 之间也可以互相组合extends。解析时按ResolvePresets算法合并依序解析所有extends插件按集合合并每个插件只出现一次。graphile-build 与 graphile-build-pggraphile-build 是一个从 plugins 构建 GraphQL.js schema 的系统特别适合自动生成的 GraphQL APIPostGraphile 即使用它也适合手写 schema 中连接connections、命名等模块化且广泛使用的关注点。graphile-build-pg 则提供理解dataplan/pg即 PostgreSQL服务的插件可为数据库资源生成类型、关系、变更mutations等。pg-sql2 与 pg-introspectionpg-sql2 是一个使用 tagged template literals 构建高度动态、防 SQL 注入的 PostgreSQL 查询的库其核心卖点是强大灵活地构建动态 SQL 而不向注入攻击敞开大门且把性能作为明确的设计目标。典型用法见 utils/pg-sql2/README.mdconst { default: sql } require(pg-sql2); // 或 import sql from pg-sql2; const tableName user; // ... 之后通过 sql... 模板拼接查询pg-introspection 是 PostgreSQL 的强类型 introspection 库依据 PostgreSQL 官方文档生成为每个 introspection 字段提供最新细节。PostGraphile 的数据库分析正是建立在它之上。jest-serializer-graphql-schema 与 graphile/lrujest-serializer-graphql-schema 是一个理解 GraphQL schema 的 Jest 序列化器避免快照被等冗长描述填满。graphile/lru 是一个近乎偏执地追求性能的 LRU 缓存README 称其可能是 Node.js 中最快的通用 LRU 缓存之一但功能集极小并坦率地建议绝大多数情况下你可能更想要 isaacs 的lru-cache而非它。Crystal 之名的由来为什么这个 monorepo 叫 crystalREADME.md 给出了有趣的答案Gra*fast*前身 DataPlanner最早的项目代号就是 Graphile Crystal。团队在正式公开前用 水晶球 emoji 作为圈内暗号指代该项目。如今 Gra*fast* 已成为规划执行引擎的正式名称而仓库里又包含不少与 GraphQL 并非严格相关的东西需要一个不那么 GraphQL 化的名字于是沿用最初的外号把 monorepo 命名为 Crystal。README 还幽默地澄清与维护者水晶婚纪念日有关的传闻纯属夸大其词。在仓库中进一步探索如果你希望深入了解上述技术栈可以从以下路径继续Gra*fast* 源码与引擎实现grafast/grafast/src/、grafast/grafast/src/execute.ts、grafast/grafast/src/engine/Gra*fast* 官方文档站点源码grafast/website/PostGraphile 插件与 preset 实现postgraphile/postgraphile/src/plugins/、postgraphile/postgraphile/src/presets/PostGraphile 测试与 schema 导出脚本postgraphile/postgraphile/tests/、postgraphile/postgraphile/scripts/test-schema-exports.mjs配置层与基础库utils/graphile-config/、utils/pg-sql2/、utils/pg-introspection/、utils/graphile-export/仓库构建与测试命令package.jsonbuild-init、build、test、postgraphile等脚本。小结Graphile Crystal monorepo 提供了一条从数据库到 GraphQL API 的完整技术链路pg-introspection负责读懂 PostgreSQLpg-sql2负责安全地构造 SQLdataplan/pg提供数据访问的 plan 步骤graphile-build/graphile-build-pg负责 schema 生成graphile-config统一插件与配置而 Gra*fast* 与 PostGraphile 分别面向自建高效 GraphQL 引擎与PostgreSQL 驱动的高性能 API两种核心诉求graphile-export则确保这一切不会把你锁死在框架里。无论你是想直接基于 PostgreSQL 快速上线一个 GraphQL API还是想为自己的 GraphQL.js schema 引入规划式执行以换取更高效率都可以在本仓库中找到对应的组件并按需独立取用。【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表