ARTICLE DETAIL

资讯详情

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

从 Graphcool Framework 迁移到 Prisma:Hooks、Resolver 函数与服务端订阅(Functions)实战指南

从 Graphcool Framework 迁移到 Prisma:Hooks、Resolver 函数与服务端订阅(Functions)实战指南 后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载本文基于 docs/1.2/04-Reference/09-Migration-Guides/04-Functions.md 展开。Graphcool Framework 中的Hooks同步数据校验与转换、Resolver Functions扩展 CRUD API 能力与服务端订阅Server-side Subscriptions三大函数机制在迁移到 Prisma 时有着清晰的对应改造路径前两者从托管函数形态下放为GraphQL 应用层Application Layer的普通 resolver后者则统一收敛为基于webhook的事件订阅。读完本文你将掌握这三类函数在两种架构下的等价实现方式并能在自己的 GraphQL Server 中落地可运行的迁移方案。迁移前提理解 Prisma 的两层 GraphQL 架构在开始逐项迁移之前需要先建立 Prisma 的架构心智模型。如 Migration Guides 总览 所述用 Prisma 构建 GraphQL 服务器时你的服务由两个 GraphQL 层组成数据库层Database Layer由 Prisma 提供本质上是 GraphQL 查询引擎对外暴露基于数据模型自动生成的通用 CRUD API可类比 Graphcool 的 Simple Relay API 的合并。应用层Application Layer这是 Graphcool 时代没有的新概念。应用层定义暴露给客户端的另一个 GraphQL API即 application schema业务逻辑、认证、权限、文件处理等全部在此层实现。迁移的核心结论是过去写在 Graphcool 函数Hooks / Resolvers里的逻辑现在全部转移到应用层的传统 GraphQL resolver 中实现并通过prisma-binding之类的库把请求转发给底层的 Prisma API。下图描述了这一职责迁移关系Graphcool Framework Prisma ───────────────── ────── Hook 函数数据校验/转换 ──→ 应用层 createUser 等 resolver Resolver 函数扩展 CRUD ──→ 应用层自定义 root field 的 resolver 托管函数/Webhook 订阅 ──→ 仅 Webhook 形式subscriptions 配置下面分别针对三类函数展开。Hooks数据校验与转换逻辑迁移到 resolverHooks 在 Graphcool Framework 中用于同步的数据校验Data Validation与数据转换Data Transformation。你可以把某个 hook 关联到 GraphQL API 的某个 mutation 上在执行 mutation 之前Graphcool 会先执行 hook 函数它可以转换传入的 mutation 参数也可以在校验规则不满足时抛错。为便于对照下文统一使用如下数据模型与 application schematype User { id: ID! unique name: String! }type Query { users: [User!]! } type Mutation { createUser(name: String!): User! }数据校验Data Validation示例场景不允许创建name少于两个字母的User节点。Graphcool Framework 中的写法在 hook 函数中实现该校验并把它关联到 Graphcool GraphQL API 的createUsermutationevent { if (event.data.name.length 2) { return { error: The provided name ${event.data.name} is too short. A name must have at least two letters. } } return { data: event.data } }这里的关键点是hook 通过返回{ error: ... }来拒绝请求通过返回{ data: event.data }放行必要时可同时改写数据。事件对象event.data携带的是即将写入的 mutation 参数。Prisma 中的等价实现在 Prisma 架构下该校验移入应用层——即你的 GraphQL Server 实现内部。更准确地说校验需要在createUser的 resolver内部完成不满足约束时直接throw一个 Errorfunction createUser(parent, { name }, context, info) { if (name.length 2) { throw new Error(The provided name ${name} is too short. A name must have at least two letters.) } return context.db.mutation.createUser({ data: { name }, info) }与 hook 的返回 error 对象不同Prisma 方案直接利用 GraphQL resolver 的异常机制。注意代码中context.db即 Prisma 客户端实例通过 prisma-binding 或 prisma-client-lib 生成最终写库操作仍委托给 Prisma 的数据库层。数据转换Data Transformation数据转换的思路类似原本在 hook 函数中的转换逻辑现在同样下放到应用层。示例场景只把name字段的值以全大写形式存储。Graphcool Framework 中的写法event { const uppercaseName event.data.name.toUppercase() return { data: uppercaseName } }Prisma 中的等价实现在createUserresolver 中先转换再写库function createUser(parent, { name }, context, info) { const uppercaseName name.toUppercase() return context.db.mutation.createUser({ data: { name: uppercaseName }, info) }细节提示原文档两处示例中的toUppercase()是 Graphcool 时代文档沿用的笔误JavaScript 标准 API 应为toUpperCase()同时createUser({ data: { name }, info)末尾缺少一个右括号}实际书写时应补全为createUser({ data: { name }, info })。在迁移到你自己的代码库时请按修正后的写法使用。Resolver Functions自定义 root field 下放为普通 resolverGraphcool Framework 中的 Resolver Functions 用于扩展自动生成的 CRUD API 能力。典型场景包括认证如signup、loginmutation、集成第三方服务、包装 REST API。下文以包装 REST API为例展开用例源自 Graphcool Framework 的rest-wrapper示例目标端点采用https://dog.ceo/api/breed/${breedName}/images/random——根据犬种名随机返回一张该犬种的图片 URL。Graphcool Framework 中的写法在 Graphcool Framework 中一个 resolver function 由两部分组成SDL 编写的 schema extension扩展Query类型并定义新的 root field供客户端发起查询JavaScript 实现的 resolver处理逻辑schema extension 如下type RandomBreedImagePayload { url: String! } extend type Query { randomBreedImage(breedName: String!): RandomBreedImagePayload! }resolver 实现则从事件对象event中取出breedName参数调用上述 REST 端点并确保返回数据符合RandomBreedImagePayload的结构require(isomorphic-fetch) module.exports event { const { breedName } event.data const url https://dog.ceo/api/breed/${breedName}/images/random return fetch(url) .then(response response.json()) .then(responseData { const randomBreedImageData responseData.message const randomBreedImage { url: randomBreedImageData } return { data: randomBreedImage } }) }注意 Graphcool 的 resolver 通过module.exports event ...的形式导出且返回结构需要包一层{ data: ... }。Prisma 中的等价实现与 Hooks 类似Graphcool 的 resolver function 能力现在在应用层实现不再由 Prisma 直接托管。首先application schema 中同样需要定义对应的 root fieldtype RandomBreedImagePayload { url: String! } extend type Query { randomBreedImage(breedName: String!): RandomBreedImagePayload! }然后resolver 只是你的 GraphQL Server 实现中的一个普通 resolver 函数这里用对象方法简写形式定义匿名函数同样适用function(parent, { breedName }, context, info) { const url https://dog.ceo/api/breed/${breedName}/images/random return fetch(url) .then(response response.json()) .then(responseData { const randomBreedImageData responseData.message const randomBreedImage { url: randomBreedImageData } return { data: randomBreedImage } }) }与 Graphcool 版本相比Prisma 版本不再需要event.data解包breedName直接作为 GraphQL 标准 resolver 的第二个参数args传入返回结构也遵循 GraphQL 标准——直接返回对象由 schema 约束其形状。服务端订阅Server-side Subscriptions收敛为 Webhook 交付服务端订阅在 Prisma 中遵循同样的订阅概念但有一个关键差异Prisma 不再支持将对应函数托管为 managed functionsGraphcool 中的托管函数而是必须通过webhook配置指向你自己部署的 HTTP 端点例如 AWS Lambda、Google Cloud Functions、Zeit Now 等。下文示例源自 Graphcool Framework 的subscriptions示例。Graphcool Framework 中的写法在 Graphcool Framework 中配置一个服务端订阅需要提供两个组件GraphQL 订阅查询subscription query定义订阅什么事件、事件发生时接收哪些数据处理器handler事件发生时被调用——既可以是 managed function也可以是 webhook以下订阅查询表达的是当一个新的User节点被创建时触发 handler事件载荷携带新User的id与namecreateFirstArticle.graphqlsubscription { User(filter: { mutation_in: [CREATED] }) { node { id name } } }接着把 handler 指定为 managed functioncreateFirstArticle.jsconst { fromEvent } require(graphcool-lib) module.exports event { // Retrieve payload from event const { id, name } event.data.User.node // Create Graphcool API (based on https://github.com/graphcool/graphql-request) const graphcool fromEvent(event) const api graphcool.api(simple/v1) // Create variables for mutation const title My name is ${name}, and this is my first article! const variables { authorId: id, title } // Create mutation const createArticleMutation mutation ($title: String!, $authorId: ID!) { createArticle(title: $title, authorId: $authorId) { id } } // Send mutation with variables return api.request(createArticleMutation, variables) }这里 handler 收到的event具有如下结构与订阅查询的形状一致{ data: { User: { node: { id: cj8wscby6nl7u0133zu7c8a62, name: Sarah } } } }在 Graphcool 中managed function 配置在graphcool.yml里指向订阅查询文件与实现文件functions: createFirstArticle: type: subscription query: src/createFirstArticle.graphql handler: code: src/createFirstArticle.jsPrisma 中的等价实现在 Prisma 中你仍然在服务根配置文件prisma.yml中配置订阅但 YAML 键名不同且 handler 只能指向 webhooksubscriptions: createFirstArticle: query: src/createFirstArticle.graphql webhook: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/createFirstArticle该示例假定你已经把一个 serverless 函数部署到了端点https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/createFirstArticle——订阅触发时Prisma 会通过 HTTP 调用该端点并把订阅查询的结果作为请求载荷发送过去。因此原先写在 managed function 里的逻辑读取event.data.User.node、调用createArticlemutation 等需要迁移到该 webhook 端点对应的函数代码中。深入 Prisma 的subscriptions配置细节源码视角为了让上文的 Prisma 配置真正可用有必要精确理解prisma.yml中subscriptions的完整语法。这一部分在两个地方有官方依据配置结构与两种 webhook 写法依据 服务配置参考 · YAML 结构 中subscriptionsoptional一节subscriptions属性用于定义服务的全部事件订阅函数每个订阅需要至少两类信息订阅查询定义哪个事件触发函数、载荷长什么样webhook 的 URL事件发生时通过 HTTP 调用的地址可选HTTP headers附加到发往该 URL 的请求上其类型为对象包含以下属性属性必填说明query是订阅查询文件的路径webhook是要调用的 webhook 信息。无 headers 时可直接给 URL 字符串需要 headers 时为一个含url与headers的对象不带 HTTP headers 的写法webhook直接给字符串subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/sendWelcomeEmail带两个 HTTP headers 的写法webhook为对象支持${env:...}变量subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: url: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/sendWelcomeEmail headers: Authorization: ${env:MY_ENDPOINT_SECRET} Content-Type: application/jsonCLI 端如何解析这些配置prisma.yml的解析在 CLI 包 prisma-yml 中完成。以 PrismaDefinition.ts 的getSubscriptions()方法为例从源码可以确认webhook字段确实支持两种形态当它是字符串时直接作为 URL且无 headersheaders为空数组当它是对象时取webhook.url并通过transformHeaders(subscription.webhook.headers)处理 headersquery字段支持文件路径与内联查询两种形态若以.graphql结尾则视为相对于prisma.yml所在目录definitionDir的文件路径读取其内容作为订阅查询读取前会做存在性校验文件不存在时抛出形如Subscription query path provided in subscription name in prisma.yml does not exist.的错误最终每个订阅被归一化为{ name, query, headers, url }的结构供部署流程使用。另外订阅查询还允许直接内联在prisma.yml中此时query后面跟的是|块标量例如服务端订阅参考文档 Server-side Subscriptions Overview 中的示例service: my-service stage: ${env:PRISMA_STAGE} secret: ${env:PRISMA_SECRET} cluster: ${env:PRISMA_CLUSTER} datamodel: database/datamodel.graphql subscriptions: userChangedEmail: webhook: url: http://example.org/sendSlackMessage headers: Content-Type: application/json Authorization: Bearer cha2eiheiphesash3shoofo7eceexaequeebuyaequ1reishiujuu6weisao7ohc query: | { user({ where: { mutation_in: [UPDATED], updatedFields_contains: email } }) { name email } }订阅查询语法的演进差异从上述内联示例可以看到Prisma 服务端订阅的查询语法已经与 Graphcool 时代的User(filter: { mutation_in: [CREATED] })不同Prisma 使用小写的根字段如user并改用where: { mutation_in: [...] }作为过滤条件。也就是说迁移订阅查询时除了把graphcool.yml换成prisma.yml、把functions键换成subscriptions键、把handler.code换成webhook之外订阅查询本身也需要按 Prisma 的 API 语法改写。该文档还提到服务端订阅在能力上等价于普通 GraphQL subscriptions支持同样的过滤条件区别仅在于交付机制Prisma 会监测数据变更并在适用时执行关联查询然后通过 webhook 把结果投递出去。迁移清单三大函数的对照速查能力Graphcool FrameworkPrisma数据校验Hookhook 函数关联 mutation返回{ error }拒绝应用层 resolver 内throw new Error(...)数据转换Hookhook 函数返回{ data: 转换后数据 }应用层 resolver 内先转换再委托 Prisma 写库扩展 CRUDResolver FunctionSDL schema extension module.exports event ...托管函数application schema 定义 root field 普通 GraphQL resolver服务端订阅managed function 或 webhook 两种 handler仅 webhook 形式prisma.yml的subscriptions键订阅查询语法User(filter: { mutation_in: [CREATED] })user(where: { mutation_in: [CREATED] })等 Prisma 语法综合来看从 Graphcool Framework 迁移到 Prisma 时函数机制的总体方向是把平台托管的业务逻辑搬回自己的应用代码。Hooks 与 Resolver Functions 对应为应用层的 GraphQL resolver业务逻辑、校验、第三方集成都留在你的 GraphQL Server 中服务端订阅则简化为订阅查询 webhook 端点的纯事件通知模型。理解并应用这一范式转换是平滑完成迁移的关键。赞分享后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载相关推荐Graphcool 到 Prisma 迁移指南FunctionsHooks、Resolver 与服务端订阅落地实践Graphcool 到 Prisma 迁移指南FunctionsHooks、Resolver 与服务端订阅落地实践 本篇指南讲解如何把 Graphcool后端数据库GraphQLGraphcool 迁移到 PrismaHooks、Resolver Functions 与 Server-side Subscriptions 函数能力迁移完整指南Graphcool 迁移到 PrismaHooks、Resolver Functions 与 Server side Subscriptions 函数能力迁移后端数据库GraphQLPrisma 迁移指南四从 Graphcool Framework 迁移函数能力Hooks、Resolver Functions 与 Server-side SubscriptionsPrisma 迁移指南四从 Graphcool Framework 迁移函数能力Hooks、Resolver Functions 与 Server si后端数据库GraphQL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表