☰
从 Graphcool 迁移到 Prisma:Hooks、Resolver 与 Server-side Subscriptions 的落地改造指南
2026/9/24 20:56:46 网站建设 项目流程
  • 后端
  • 数据库
  • GraphQL

【免费下载链接】prisma1

💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]

项目地址:https://gitcode.com/gh_mirrors/pr/prisma1
点击查看免费下载

在 Graphcool Framework 中,业务逻辑可以通过 Hooks(数据校验/转换)、Resolver Functions(自定义解析器)与 Server-side Subscriptions(服务端订阅)三类 serverless 函数挂载到 GraphQL API 上。迁移到 Prisma 后,这些函数的能力并没有消失,而是改变了两点:一方面,Hooks 与 Resolver Functions 的实现位置从"平台托管"下沉到了应用层(你自己的 GraphQL Server resolver);另一方面,服务端订阅的触发方式统一收敛为Webhook,指向你自行部署的 HTTP 端点。本文以 04-Functions.md 为主线,逐一给出三类功能的迁移前后对照示例,并结合本仓库中 prisma.yml 订阅配置说明、服务端订阅参考文档 以及 CLI 解析源码 PrismaDefinition.ts 做纵深佐证。读完本文,你将能熟练地把 Graphcool 项目中的函数逻辑迁移到 Prisma 的 resolver 与 webhook 模型中,并正确书写prisma.yml的subscriptions配置。

迁移总览:三类函数分别去向何处

Graphcool Framework 的函数体系包含三类能力,它们的"职责边界"在迁移前后对比如下:

Graphcool 函数类型用途迁移后的落点
Hooks对 mutation 做同步的数据校验与转换应用层 GraphQL resolver 内部(先处理再调 Prisma 客户端)
Resolver Functions扩展自动生成的 CRUD API(如认证、包装 REST API)应用层 GraphQL resolver(schema 扩展 + 普通 JS 函数实现)
Server-side Subscriptions订阅数据变更事件并触发处理逻辑由 Prisma 通过 Webhook 推送事件到自部署的 HTTP 端点

其背后的核心思想是:Prisma 专注于数据层的 GraphQL API,不再托管业务函数;所有与"业务规则"相关的同步逻辑都应在你的应用服务器中执行,Prisma 仅负责通过其生成的客户端暴露数据操作能力。整个迁移系列的其他主题(数据建模、认证鉴权、文件处理、服务器托管)可参见 Graphcool 迁移指南目录。

为便于说明,本文假设的数据模型(datamodel)如下:

type User { id: ID! @unique name: String! }

并假设你的 GraphQL 服务器对外暴露如下应用 schema(application schema):

type Query { users: [User!]! } type Mutation { createUser(name: String!): User! }

Hooks 的迁移:业务规则下沉到 resolver

Graphcool 中的 Hooks 用于同步的数据校验与数据转换。你可以把某个 hook 关联到 GraphQL API 的某个 mutation:在 mutation 真正执行之前,Graphcool 平台会先执行该 hook 函数,函数可以选择转换传入的 mutation 参数,也可以在校验规则被违反时抛错。

迁移到 Prisma 后,这些逻辑全部移入应用层——即你的 GraphQL Server 实现之中,具体位置是createUser这样的 resolver 内部。

数据校验(Data validation)迁移

示例场景:不允许name少于两个字母的User节点被创建。

Graphcool Framework 中的写法:把下述函数关联到 Graphcool GraphQL API 的createUsermutation 上,通过返回值中的error字段表达校验失败,通过data字段透传合法数据:

event => { 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 } }

Prisma 中的写法:校验逻辑移入应用层 resolver,校验失败直接throw new Error(...),通过后调用 Prisma 生成的客户端context.db.mutation.createUser真正写入数据(原文档代码中info前缺少一个闭合括号,此处为可运行版本):

function 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) }

注意这里context.db即 Prisma 客户端实例,它由datamodel自动生成(相关实现可参考仓库中的 prisma-client-lib)。错误信息通过 GraphQL 标准的Error机制返回给客户端,而不是像 Graphcool 那样约定一个{ error }返回结构。

数据转换(Data transformation)迁移

示例场景: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()为笔误,JavaScript 的正确方法名是String.prototype.toUpperCase();迁移到生产代码时请使用后者。无论哪种写法,核心模式一致——所有"入库前"的加工都发生在 resolver 的调用链上,Prisma 客户端只负责执行最终的数据变更。

Resolver Functions 的迁移:从 schema 扩展 + JS 函数到普通 resolver

Graphcool 中的 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 中,一个 resolver function 由两部分组成:

  • 用 SDL 编写的schema 扩展(schema extension);
  • 用 JavaScript 编写的resolver 实现(resolver implementation)。

schema 扩展需要扩展Query类型并定义一个新的根字段,供客户端发起查询:

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 } }) }

Prisma 的写法:纯应用层 resolver

与 Hooks 类似,Graphcool Resolver Functions 的能力在 Prisma 中不再由平台承担,而是直接在应用层实现。应用 schema 中仍需定义对应的根字段:

type RandomBreedImagePayload { url: String! } extend type Query { randomBreedImage(breedName: String!): RandomBreedImagePayload! }

resolver 则成为你的 GraphQL Server 实现中的一个普通函数(与 Prisma 客户端无关,纯业务代码):

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 } }) }

对比可见:事件驱动的module.exports = event => ...形态变成了标准 GraphQL resolver 的(parent, args, context, info)形态,参数从event.data解构改为直接从 resolver 的第二个参数解构。整个迁移过程中,业务实现本身几乎原样保留,变化的只是"挂载方式"。

Server-side Subscriptions 的迁移:从 managed function 到 webhook

服务端订阅(server-side subscription)在 Prisma 中延续了与 GraphQL Subscription 等价的能力与 API:你可以使用相同的过滤器(filter),只订阅感兴趣的事件。二者的差别在于事件投递机制。在 Graphcool Framework 中,处理函数可以是平台托管的managed function,也可以是 webhook;而在 Prisma 中,函数无法再以 managed function 形式托管,必须通过webhook配置——指向你自己部署的 HTTP 端点(例如 AWS Lambda、Google Cloud Functions、Zeit Now 等)。

关于订阅的完整语义可参考 服务端订阅概述:Prisma 会持续监听数据变更,在满足条件时执行关联的订阅查询,再通过 webhook 把事件载荷推送给你的端点。

下文示例基于 Graphcool Framework 的subscriptions示例改造:当新的User节点被创建时,自动为一篇新文章生成标题。

订阅查询:两种框架下共用同一份

Graphcool 中配置一个服务端订阅需要两个组成部分:

  • 一段GraphQL 订阅查询,定义订阅什么事件、事件发生时想收到什么数据;
  • 一个handler(managed function 或 webhook),在事件发生时被调用。

下面的订阅查询表达:当一个新的User节点被**创建(CREATED)**时触发 handler,事件载荷携带新User的id与name:

createFirstArticle.graphql

subscription { User(filter: { mutation_in: [CREATED] }) { node { id name } } }

Graphcool Framework:managed function 形态

Graphcool 中把 handler 指定为 managed function:

createFirstArticle.js

const { fromEvent } = require('graphcool-lib') module.exports = event => { // Retrieve payload from event const { id, name } = event.data.User.node // Create Graphcool API (based on 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) }

注意:传入的event结构严格遵循订阅查询的形状,如下所示(node内的id与name正是订阅查询中请求的字段):

{ "data": { "User": { "node": { "id": "cj8wscby6nl7u0133zu7c8a62", "name": "Sarah" } } } }

Graphcool 在graphcool.yml中通过functions键配置该订阅函数,指向订阅查询文件与 managed function 的实现文件:

functions: createFirstArticle: type: subscription query: src/createFirstArticle.graphql handler: code: src/createFirstArticle.js

Prisma:webhook 形态的订阅配置

迁移到 Prisma 后,订阅仍然配置在服务根配置文件prisma.yml中,但 YAML 键发生了变化,且 handler只能指向 webhook:

subscriptions: 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这个 HTTP 端点。部署在端点上的函数收到 POST 请求后,从事件载荷中取出User.node数据,执行与上节相同的"创建第一篇文章"逻辑(原先由graphcool-lib的fromEvent提供的能力,现在由 webhook 接收到的 HTTP 请求体承担)。

webhook 配置的两种形态

根据仓库文档 prisma.yml 的 YAML 结构 中的subscriptions一节,subscriptions属性接收一个对象,每个订阅至少包含:

  • query(必填):订阅查询文件的路径;
  • webhook(必填):被调用的 webhook 信息。如果没有自定义 headers,可以直接把 URL 字符串赋给webhook(如上例);如果需要携带 HTTP headers,则webhook是一个包含url与headers的对象。

无 headers 的简写形式:

subscriptions: sendWelcomeEmail: query: database/subscriptions/sendWelcomeEmail.graphql webhook: https://bcdeaxokbj.execute-api.eu-west-1.amazonaws.com/dev/sendWelcomeEmail

带 headers 的完整形式(可用于 webhook 端点的鉴权,如Authorization、Content-Type):

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/json
内联查询与自定义变量

query除了指向.graphql文件,也可以直接内联一段订阅查询(参考 服务端订阅示例 中的完整prisma.yml配置):

subscriptions: userChangedEmail: webhook: url: http://example.org/sendSlackMessage headers: Content-Type: application/json Authorization: Bearer cha2eiheiphesash3shoofo7eceexaequeebuyaequ1reishiujuu6weisao7ohc query: | subscription { user(where: { mutation_in: [UPDATED] }) { node { name email } } }

同时,subscriptions中的值也支持self变量引用——可以把 webhook 端点与查询目录抽象为custom变量再复用(参见 02-YAML-Structure.md 中的custom示例)。

源码视角:CLI 如何解析订阅配置

Prisma CLI 在解析prisma.yml时,由 PrismaDefinition.ts 中的getSubscriptions()方法统一处理订阅配置,其行为与上述文档完全吻合,可作为配置合法性的源码级依据:

  • webhook可以是字符串(直接作为 URL)或对象(取其url字段);若为对象,则通过transformHeaders把headers转换为请求头列表;
  • query若以.graphql结尾,会被当作相对prisma.yml所在目录的文件路径处理:若文件不存在,会抛出形如Subscription query <path> provided in subscription "<name>" in prisma.yml does not exist.的错误;若存在则读取文件内容作为订阅查询;
  • 解析结果被整理为{ name, query, headers, url }结构的函数输入(FunctionInput),供后续部署流程使用。

也就是说,从源码结构可以推断:CLI 只负责把订阅定义翻译成"订阅查询 + webhook URL + headers"三元组,真正的事件检测、载荷构造与 HTTP 投递由 Prisma 服务端完成。

迁移自查清单与后续参考

完成三类函数迁移后,建议按如下清单核对:

  1. Hooks 是否全部落地到 resolver:所有原先挂在 mutation 上的校验/转换逻辑,确认已移入对应 resolver 的调用链,且通过抛Error表达校验失败;
  2. Resolver Functions 的 schema 扩展是否保留:应用 schema 中的扩展根字段(如randomBreedImage)需要保留,仅实现层从"事件函数"改为"标准 resolver";
  3. Subscriptions 是否已切换到 webhook:graphcool.yml的functions配置已改为prisma.yml的subscriptions配置,handler 从 managed function 改为自部署的 HTTP 端点,必要时配置headers完成端点鉴权;
  4. 订阅查询文件路径是否存在:query指向的.graphql文件必须真实存在(源码会校验并报错),且订阅查询的字段形状决定 webhook 收到的载荷结构。

本文聚焦函数体系的迁移;数据建模与 GraphQL API 形态的对应关系、认证鉴权策略、文件处理与服务器托管方案的迁移,分别参见迁移指南中的 02-Data-Modelling & GraphQL-API.md、03-Authentication & Authorization.md、05-File-Handling.md 与 06-Server-Hosting.md。如需查阅订阅 webhook 的完整行为语义与触发示例 mutation,可继续阅读 服务端订阅概述。

  • 后端
  • 数据库
  • GraphQL

【免费下载链接】prisma1

💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]

项目地址:https://gitcode.com/gh_mirrors/pr/prisma1
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询