ARTICLE DETAIL

资讯详情

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

Prisma 生态中的 graphql-binding:用模块化 GraphQL Binding 复用与组合已有 API

Prisma 生态中的 graphql-binding:用模块化 GraphQL Binding 复用与组合已有 API 后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载GraphQL bindings 是一种将既有 GraphQL API「封装」为可复用、可组合模块的技术——把 API 的根字段转变成编程语言中可直接调用的函数。本文以 Prisma 仓库文档 docs/1.3/06-GraphQL-Ecosystem/02-GraphQL-Binding/01-GraphQL-Binding.md 为主体结合 prisma-client-lib 的源码实现系统讲解graphql-binding的安装、Binding API构造器、query/mutation、subscription、最小可运行示例以及它在 Prisma 生态中的直接落地形式prisma-binding。读完本文你将掌握如何把任意 GraphQL API 包装成一个Binding实例、如何在 resolver 中委托查询/变更并理解 Binding 背后的委托执行机制。一、什么是 GraphQL Bindinggraphql-binding是一个简化「创建自己的 GraphQL binding」过程的 npm 包。GraphQL bindings 本质上是模块化构建块它允许你把既有的 GraphQL API 嵌入到自己的 GraphQL 服务器中。可以把它想象成把部分GraphQL API 变成可复用的 LEGO 积木。在 Prisma 的架构中这一思想被大量应用Prisma 服务暴露出一套完整的 CRUD GraphQL API对应database.graphql开发者通过 binding 将这套 API 的函数化接口嵌入自己的应用层 schema从而避免手写 SQL、也避免手拼 GraphQL 查询字符串。正如 docs/1.3/04-Reference/01-Introduction/02-GraphQL-Server-Development.md 所描述的bindings 允许通过调用与 schema 根字段同名的函数来发送查询与变更例如binding.mutation.createUser({ name: Sarah }, { id }) binding.query.users({}, { id name })调用这些方法时binding 实例在底层负责把操作翻译成 GraphQL 查询、把查询发送到服务器并把响应以编程语言对象的形式返回给你。二、安装graphql-binding通过 npm 包管理工具安装即可yarn add graphql-binding它依赖graphql与graphql-tools用于makeExecutableSchema等 schema 工具函数。在 Prisma 生态中若使用 Prisma 客户端则会在 cli/packages/prisma-client-lib/package.json 中看到一组相关的依赖如graphql-tag、subscriptions-transport-ws等它们共同支撑起 binding 模式的运行时。三、Binding APIgraphql-binding的核心导出是Binding类其完整 API 如下。3.1 构造器constructor(options: BindingOptions): BindingBindingOptions具有以下属性KeyRequiredTypeDefaultNoteschemaYesGraphQLSchema-用于 binding 的可执行 GraphQL schemafragmentReplacementsNoFragmentReplacements{}一组 GraphQL fragment 定义用于指定 resolver 正常工作所必需的字段beforeNo() void(() undefined)在查询/变更发送到 GraphQL API 之前执行的函数handlerNoanynull来自 JS Proxy 的handler对象subscriptionHandlerNoanynull订阅subscription专用的 handler其中schema是必须提供的可执行 schema——它定义了 binding 将暴露哪些根字段fragmentReplacements用于解决「resolver 需要某些字段但查询未请求」的问题让 binding 自动把这些 fragment 合并进委托查询。3.2 query 与 mutationbinding.query.rootField: QueryMapany // rootField 是映射 schema 中 Query 类型上的字段名 binding.mutation.rootField: QueryMapany // rootField 是映射 schema 中 Mutation 类型上的字段名binding对象暴露两个用于向 API 发送查询与变更的属性binding.query和binding.mutation。二者类型均为QueryMap暴露的方法以 schema 中Query与Mutation类型的根字段命名。这些方法接收三个参数NameRequiredTypeNoteargsNo[key: string]: any包含根字段参数的对象contextNo[key: string]: any沿 GraphQL resolver 链传递的context对象每个 resolver 都可读写该对象infoNoGraphQLResolveInfo|string沿 resolver 链传递的info对象包含传入查询/变更的 AST或包含 selection set 的字符串示例假设存在如下 schematype Query { user(id: ID!): User } type Mutation { createUser(): User! }如果存在一个实现该 schema 的 GraphQL API 的 binding你可以调用binding.query.user({ id: abc }) binding.mutation.createUser()在 resolver 实现中使用 binding 时可以这样委托findUser(parent, args, context, info) { return binding.user({ id: args.id }, context, info) } newUser(parent, args, context, info) { return binding.createUser({}, context, info) }注意这里 resolver 的infoGraphQLResolveInfo被直接透传给 binding 方法binding 据此自动构建完整的 selection set使委托查询只请求客户端真正需要的字段——这正是 binding 相比「手拼查询字符串」更优雅的核心原因。3.3 subscriptionbinding.subscription.rootField(...): AsyncIteratorany | PromiseAsyncIteratorany // rootField 是映射 schema 中 Subscription 类型上的字段名binding.subscription与query、mutation思路相同但返回的不是单个值Promise而是值的流AsyncIterator。其类型为SubscriptionMap暴露的方法以 schema 中Subscription类型的根字段命名这些方法的三个参数与query/mutation生成的方法完全一致。在 Prisma 客户端实现中订阅流经过了专门的适配见 cli/packages/prisma-client-lib/src/utils/observableToAsyncIterable.ts把 subscription-transport-ws 的 Observable 转换为 AsyncIterator以及 cli/packages/prisma-client-lib/src/utils/mapAsyncIterator.ts对流的每个元素做 payload 提取映射。四、最小可运行示例下面是一个完整的、可直接运行的最小示例展示如何把本地 schema 包装成 binding 并调用const { makeExecutableSchema } require(graphql-tools) const { Binding } require(graphql-binding) const users [ { name: Alice, }, { name: Bob, }, ] const typeDefs type Query { findUser(name: String!): User } type User { name: String! } const resolvers { Query: { findUser: (parent, { name }) users.find(u u.name name), }, } const schema makeExecutableSchema({ typeDefs, resolvers }) const findUserBinding new Binding({ schema, }) findUserBinding.findUser({ name: Bob }) .then(result console.log(result))示例中先通过graphql-tools的makeExecutableSchema构建一个可执行 schema再把它交给new Binding({ schema })。由此得到的findUserBinding即可像调用普通 JavaScript 函数一样查询findUser根字段。五、binding 模式在 Prisma 客户端中的源码印证从源码结构看Prisma 生成的客户端与graphql-binding遵循同一套「指令收集 委托执行」设计。以 cli/packages/prisma-client-lib/src/Client.ts 为例方法暴露Client类在buildMethods()中把 schema 的Query/Mutation类型字段Object.assign到自身$subscribe指向Subscription类型见buildMethods()与getTypes()这与 binding 的query/mutation/subscription三通道结构一一对应。指令累积每次链式调用如prisma.query.users(...).then(...)都会向_currentInstructions中压入一条Instruction含fieldName、args、field、typeName、可选fragment然后在then/catch时统一处理。查询组装generateSelections()通过reduceRight反向遍历指令把用户级联调用的字段逐层嵌套进 selection set参数自动转换为 GraphQL 变量variableDefinitions从而生成完整操作文档。payload 提取extractPayload()沿结果逐层下钻剥离__typename包装并返回最终对象订阅场景则由mapSubscriptionPayload()用mapAsyncIterator对结果流做同样的提取。动态委托约定getTypes()中还实现了便捷参数约定——Mutation下create*字段自动把参数包装为{ data: ... }、delete*包装为{ where: ... }Query/Subscription单参数字段包装为{ where: ... }。这些行为印证了prisma.query.user({ where: { id: abc } }, { name })这类调用风格。因此可以说Prisma 客户端本质上就是一个针对 Prisma 服务 schema 特化的动态 binding 实例graphql-binding定义的概念在其生产实现中得到了完整落地。六、从通用 binding 到 prisma-bindinggraphql-binding是通用底层包而在 Prisma 生态中更常用的是它的特化产物——prisma-binding参见姊妹文档 docs/1.3/06-GraphQL-Ecosystem/02-GraphQL-Binding/02-Prisma-Binding.md。prisma-binding为 Prisma 服务提供专属的 GraphQL binding它通过委托执行简化 resolver 实现把查询/变更的执行委托给底层 Prisma 数据库服务的 API。工作流程如下通过定义数据模型创建 Prisma 服务下载生成的数据库 schema 定义database.graphql包含完整 CRUD API定义应用层 schema通常命名为app.graphql用 Prisma 服务的信息endpoint、schema 路径等实例化Prisma实现应用层 schema 的 resolver通过生成的委托 resolver 函数委托到底层 Prisma 服务。安装方式yarn add prisma-binding # 或 npm install --save prisma-binding使用示例假设 Prisma 服务的数据模型为type User { id: ID! unique name: String }实例化Prisma后即可发送如下查询/变更// 基于具体服务实例化 Prisma const prisma new Prisma({ typeDefs: schemas/database.graphql, endpoint: https://api.graph.cool/simple/v1/my-prisma-service secret: my-super-secret-secret }) // 查询指定用户只取 name 字段 prisma.query.user({ where: { id: abc } }, { name }) // 查询所有用户的 id 与 name prisma.query.users(null, { id name }) // 创建名为 Sarah 的用户并返回 id prisma.mutation.createUser({ data: { name: Sarah } }, { id }) // 更新指定用户的名字并返回 id prisma.mutation.updateUser({ where: { id: abc }, data: { name: Sarah } }, { id }) // 删除指定用户并返回 name prisma.mutation.deleteUser({ where: { id: abc } }, { id })这些函数调用在底层都会被翻译为针对 Prisma 服务的实际 HTTP 请求。Prisma构造器参数PrismaOptions如下KeyRequiredTypeDefaultNoteschemaPathYesstring-Prisma 服务 schema 定义的文件路径通常为database.graphqlendpointYesstring-Prisma 服务的 endpointsecretYesstring-Prisma 服务的 secretfragmentReplacementsNoFragmentReplacementsnull指定 resolver 正常工作所需字段的 GraphQL fragment 定义列表debugNobooleanfalse把全部查询/变更打印到控制台Prisma实例还暴露exists属性每类型一个函数接收where对象、返回 boolean用于判断特定节点是否存在与request方法直接发送完整 GraphQL 查询/变更字符串内部基于graphql-request。例如// 判断是否存在 id 为 abc 且 author.name 为 Sarah 的 Post prisma.exists.Post({ id: abc, author: { name: Sarah } })const query query ($userId: ID!){ user(id: $userId) { id name } } const variables { userId: abc } prisma.request(query, variables) .then(result console.log(result)) // 示例结果 // {data: { user: { id: abc, name: Sarah } } }七、静态 binding 与动态 binding结合 Prisma 生态的实践GraphQL bindings 还分为两类静态 binding用于 TypeScript、Scala 等静态强类型语言。binding 函数在构建期通过代码生成产生所有调用都能被编译器校验类型错误在编译期即被捕获编辑器还能提供操作与参数的自动补全。Prisma 仓库中 cli/packages/prisma-client-lib/src/codegen 下的typescript-client.ts、flow-client.ts、go-client.ts等生成器即服务于此类场景。动态 binding常用于 JavaScript 等动态语言无需额外构建步骤方法调用在运行时才翻译成 GraphQL 查询。上一节的prisma.query.user({...}, { name })风格即属此类。两种方式共享 binding 简洁的调用语法差异在于类型安全与自动补全能力的获取时机。八、公共 GraphQL bindings 与生态参考graphql-binding生态中已有实践性、生产级的示例graphql-binding-github针对 GitHub GraphQL API 的 bindinggraphcool-bindinggraphcool 服务对应的 binding。如果基于本包创建了自己的 GraphQL binding可通过 PR 补充到列表。若想深入了解 Binding 的委托执行原理可以继续阅读仓库中的相关文档docs/1.3/04-Reference/01-Introduction/02-GraphQL-Server-Development.md「GraphQL bindings - The better ORM」一节与 docs/1.3/04-Reference/01-Introduction/03-Architecture.md应用层如何通过 binding 连接 Prisma。赞分享后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载相关推荐GraphQL Binding 指南使用 graphql-binding 将 GraphQL API 组合为可复用模块Prisma 生态实践GraphQL Binding 指南使用 graphql binding 将 GraphQL API 组合为可复用模块Prisma 生态实践 graphq后端数据库GraphQLPrisma 生态中的 graphql-binding把 GraphQL API 变成可复用的模块化构建块Prisma 生态中的 graphql binding把 GraphQL API 变成可复用的模块化构建块 本指南以 Prisma 仓库中 GraphQL B后端数据库GraphQL使用 graphql-binding 构建可复用 GraphQL API 模块Prisma 生态中的绑定实践指南使用 graphql binding 构建可复用 GraphQL API 模块Prisma 生态中的绑定实践指南 GraphQL Binding 是一类 模块后端数据库GraphQL上一篇G-Helper启动故障终极解决手册从诊断到系统修复下一篇Miles磁盘卸载Disk Offload实战NVMe流式备份让大模型装进单节点创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表