- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
在 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.jsPrisma: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 服务端完成。
迁移自查清单与后续参考
完成三类函数迁移后,建议按如下清单核对:
- Hooks 是否全部落地到 resolver:所有原先挂在 mutation 上的校验/转换逻辑,确认已移入对应 resolver 的调用链,且通过抛
Error表达校验失败; - Resolver Functions 的 schema 扩展是否保留:应用 schema 中的扩展根字段(如
randomBreedImage)需要保留,仅实现层从"事件函数"改为"标准 resolver"; - Subscriptions 是否已切换到 webhook:
graphcool.yml的functions配置已改为prisma.yml的subscriptions配置,handler 从 managed function 改为自部署的 HTTP 端点,必要时配置headers完成端点鉴权; - 订阅查询文件路径是否存在:
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]
相关推荐
Graphcool 迁移到 Prisma:Hooks、Resolver Functions 与 Server-side Subscriptions 函数能力迁移完整指南
Graphcool 迁移到 Prisma:Hooks、Resolver Functions 与 Server side Subscriptions 函数能力迁移
后端数据库GraphQLGraphcool 到 Prisma 的 Functions 迁移指南:Hooks、Resolver 与 Server-side Subscriptions 的完整落地方案
Graphcool 到 Prisma 的 Functions 迁移指南:Hooks、Resolver 与 Server side Subscriptions 的
后端数据库GraphQLPrisma 迁移指南(四):从 Graphcool Framework 迁移函数能力(Hooks、Resolver Functions 与 Server-side Subscriptions)
Prisma 迁移指南(四):从 Graphcool Framework 迁移函数能力(Hooks、Resolver Functions 与 Server si
后端数据库GraphQL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考