GraphQL Yoga 仓库实战:基于 Envelop 与 graphql-ws 构建 WebSocket GraphQL 订阅服务
后端API设计【免费下载链接】graphql-yoga Rewrite of a fully-featured GraphQL Server with focus on easy setup, performance great developer experience. The core of Yoga implements WHATWG Fetch API and can run/deploy on any JS environment.项目地址https://gitcode.com/gh_mirrors/gr/graphql-yoga点击查看免费下载本文以 graphql-yoga 仓库中examples/envelop目录下的 graphql-ws 示例为核心讲解如何用 EnvelopYoga 生态的请求编排层配合graphql-ws库搭建一个支持 Subscription 的 WebSocket GraphQL 服务。读完本文你可以完整复现该示例的运行流程理解envelop()返回的每次请求级 APIparse/validate/contextFactory/execute/subscribe如何注入到graphql-ws的onSubscribe钩子中并掌握端口、路径等关键配置。示例定位Envelop 与 WebSocket 协议层的关系在 graphql-yoga 仓库中packages/envelop是独立维护的核心包集合core、types、plugins 等examples/envelop目录则提供了一批“Envelop 与不同 GraphQL 协议/宿主组合”的可运行示例graphql-ws 示例 就是其中之一。它的定位很明确不经过 HTTPgraphql-ws官方 WebSocket 订阅协议库通过useServer直接挂载到 Node.js 的wsWebSocket 服务上Envelop 负责请求编排schema、解析器、校验、上下文构造等逻辑全部由envelop()返回的getEnveloped按请求这里按 WebSocket 连接组装示例最小化整个服务端只有一个入口文件 index.ts便于理解协议层与 Envelop 的接线方式。示例自带的 README 说明了运行前提与验证方式下面的内容将 README 中的三个步骤展开并逐段解析入口源码。运行示例依赖安装与启动步骤README 给出的运行流程共三步在仓库根目录用pnpm安装全部依赖。本仓库是 pnpm workspace 工程根目录有 pnpm-workspace.yaml示例包envelop-examples/graphql-ws通过通配依赖引用本仓库内的envelop/core{ dependencies: { envelop/core: *, graphql-tools/schema: 10.0.31, graphql: 17.0.2, graphql-ws: ^6.0.0, ws: 8.20.1 }, scripts: { start: ts-node index.ts } }以上片段来自 package.json。注意几个关键点envelop/core: *表示由 workspace 解析到本仓库packages/envelop/core的本地版本因此必须在仓库根目录执行安装单独在该目录pnpm install无法解析这个依赖运行时脚本是ts-node index.ts即直接用 ts-node 编译执行 TypeScript 入口无需预先构建graphql-ws^6.0.0是 WebSocket 协议实现ws8.20.1提供 Node.js 侧的 WebSocket 服务器。进入示例目录并启动服务cd examples/envelop/graphql-ws pnpm run start启动后 WebSocket 服务监听在本机3415 端口、/graphql路径这两个值定义在入口文件的ws.Server配置中见下文。用 GraphiQL 验证连接README 指引使用graphql-ws项目官方维护的 “GraphiQL graphql-ws” 客户端示例页该示例是一个可直接在浏览器打开的 HTML 文件在graphql-ws项目的仓库资料中提供将其中的 WebSocket URL 改为ws://localhost:3415/graphql在浏览器中打开即可。在该页面执行订阅操作后你会每秒收到一条来自服务端的问候消息与下文Subscription解析器的行为一一对应。源码解析从 Schema 到 WebSocket 服务器的完整接线整个服务端逻辑集中在 index.ts可以按“Schema 定义 → Envelop 装配 → graphql-ws 服务器挂载”三段来读。1. 定义一个带 Subscription 的可执行 Schema示例用graphql-tools/schema的makeExecutableSchema构造 schemaindex.ts 第 7-31 行const schema makeExecutableSchema({ typeDefs: /* GraphQL */ type Query { hello: String! } type Subscription { greetings: String! } , resolvers: { Query: { hello: () Hello World!, }, Subscription: { greetings: { subscribe: async function* sayHiIn5Languages() { for (const hi of [Hi, Bonjour, Hola, Ciao, Zdravo]) { yield { greetings: hi }; await new Promise(resolve setTimeout(resolve, 1000)); // wait 1 second } }, }, }, }, });Subscription.greetings的解析器是一个异步生成器依次产出 5 条不同语言的问候Hi / Bonjour / Hola / Ciao / Zdravo每 1 秒推送一条共 5 秒后结束。这正是 GraphQL Subscription 的最小实现形态——解析器返回 AsyncIterable服务端把每次yield的值作为一条next消息推给客户端。2. 装配 Envelopenvelop()与getEnvelopedconst getEnveloped envelop({ parse, validate, execute, subscribe, plugins: [useSchema(schema), useLogger()], });index.ts 第 33-39 行这里直接传入graphql包的四个引擎函数parse / validate / execute / subscribe作为默认实现并用两个插件完成装配useSchema(schema)把上面定义的 schema 注入 Envelop。从 use-schema.ts 的实现看该插件只是通过onPluginInit钩子调用setSchema(schema)把 schema 写入编排器状态之后每次请求拿到的schema都来自这里useLogger()打印每次解析后的操作便于在终端观察订阅请求。envelop()的返回值并不是“已经执行好的结果”而是一个getEnveloped函数。从 create.ts 的源码看envelop()先根据插件列表创建 Envelop 编排器orchestrator与 instrumentation然后返回一个getEnveloped(initialContext)调用getEnveloped时才会运行各插件的初始化逻辑并返回一组绑定到当前请求上下文的 APIreturn { parse: ..., validate: ..., contextFactory: ..., execute: ..., subscribe: ..., schema: ..., };也就是说每调用一次getEnveloped(ctx)就得到一套针对该连接/请求定制的 parse、validate、contextFactory、execute、subscribe 和 schema插件可以在每个钩子init、parse、context、execute、validate 等上改写这组函数的行为。这个“一次连接、一次装配”的模型正是 Envelop 适配 WebSocket 这类长连接协议的关键——HTTP 场景中按请求装配WebSocket 场景则按连接装配。3. 挂载useServer把 Envelop 接入 graphql-wsuseServer( { execute: (args: any) args.rootValue.execute(args), subscribe: (args: any) args.rootValue.subscribe(args), onSubscribe: async (ctx, msg) { const { schema, execute, subscribe, contextFactory, parse, validate } getEnveloped({ connectionParams: ctx.connectionParams, socket: ctx.extra.socket, request: ctx.extra.request, }); const args { schema, operationName: msg.payload.operationName, document: parse(msg.payload.query), variableValues: msg.payload.variables, contextValue: await contextFactory(), rootValue: { execute, subscribe, }, }; const errors validate(args.schema, args.document); if (errors.length) return errors; return args; }, }, new ws.Server({ port: 3415, path: /graphql, }), );index.ts 第 41-74 行这段接线可以分为四层理解1WebSocket 服务器配置。useServer的第二个参数是ws库创建的服务器实例port: 3415、path: /graphql决定了客户端必须连接ws://localhost:3415/graphql。README 中要求的 GraphiQL URL 正是由此而来。2onSubscribe每次订阅建立时的入口钩子。graphql-ws在客户端发送subscribe消息后调用onSubscribe(ctx, msg)其中ctx.connectionParams是客户端连接参数ctx.extra.socket/ctx.extra.request分别携带底层 WebSocket 连接与 HTTP 升级请求这两个字段是useServer对ws的ws/req对象做的透传。钩子有两种返回值返回错误数组或 falsy 值→ 拒绝该订阅错误会作为error消息发回客户端返回参数对象 →graphql-ws用其中的schema、document、variableValues、operationName、contextValue、rootValue去执行订阅Query 操作同理走execute。3getEnveloped的注入点。示例把connectionParams、socket、request放进初始上下文交给getEnveloped之后插件在onEnveloped/onContext等钩子里就能按连接定制行为例如基于connectionParams做鉴权、基于socket记录日志。返回的contextFactory被await后作为contextValue使用——Envelop 的上下文钩子链onContext就发生在这一次调用内部。4rootValue 的技巧。execute/subscribe被放进rootValue而顶层配置又写为execute: (args) args.rootValue.execute(args)、subscribe: (args) args.rootValue.subscribe(args)。graphql-ws的execute/subscribe回调在每次消息执行时才被调用此时args.rootValue正是onSubscribe返回的对象——这样每次 Query/Subscription 执行拿到的都是 Envelop 编排过、绑定到当前连接的execute/subscribe而不是模块加载时的原始graphql引擎函数。5校验时机。validate(args.schema, args.document)在onSubscribe内同步执行若有错误直接返回错误数组拒绝订阅。这里用的是 Envelop 返回的validate因此插件追加的自定义校验规则同样会在订阅建立前生效。4. 客户端视角订阅会收到什么在 GraphiQLgraphql-ws 客户端模式中执行subscription { greetings }服务端异步生成器每秒yield一次客户端会依次收到 5 条消息Hi、Bonjour、Hola、Ciao、Zdravo5 秒后订阅自然结束。若执行query { hello }则得到Hello World!——该 Query 同样经过 Envelop 编排后的execute执行。关键参数与复用要点汇总配置/参数取值本示例出处说明WebSocket 端口3415index.tsws.Server({ port })客户端 URL 需一致路径/graphql同上完整端点为ws://localhost:3415/graphql依赖版本graphql-ws^6.0.0、ws8.20.1、graphql17.0.2package.json升级协议库时注意useServer选项兼容性运行方式ts-node index.ts同上需先在仓库根目录用 pnpm 安装 workspace 依赖Envelop 插件useSchema(schema)、useLogger()index.ts可扩展为鉴权、限流等插件见packages/envelop/plugins复用这个骨架时的几个要点保持“每次连接调用一次getEnveloped这样插件上下文connectionParams、socket 等天然按连接隔离鉴权放在onSubscribegraphql-ws的onSubscribe返回错误即拒绝订阅这是该协议推荐的鉴权位置也可以借助 Envelop 插件如packages/envelop/plugins下的generic-auth、disable-introspection以插件形式实现生产环境注意ws的路径约束ws.Server要求显式path不要省略否则非 WebSocket 的 HTTP 请求处理行为受限该示例刻意不含 HTTP 端点。若同一服务还要提供 GraphQL over HTTP可参考仓库examples/envelop/graphql-http等其他示例的组合方式或直接在 Yoga 中集成graphql-ws协议。小结这个示例用不到 75 行代码展示了 Envelop 在长连接协议下的标准接线方式envelop()负责装配出按连接定制的 parse/validate/execute/subscribe 管线graphql-ws的useServer在onSubscribe钩子中消费这条管线ws.Server决定监听地址。运行入口与验证方式见 examples/envelop/graphql-ws/README.md完整源码见 examples/envelop/graphql-ws/index.tsEnvelop 核心装配逻辑可继续深入 packages/envelop/core/src/create.ts 与 packages/envelop/core/src/plugins/use-schema.ts 查阅。赞分享后端API设计【免费下载链接】graphql-yoga Rewrite of a fully-featured GraphQL Server with focus on easy setup, performance great developer experience. The core of Yoga implements WHATWG Fetch API and can run/deploy on any JS environment.项目地址https://gitcode.com/gh_mirrors/gr/graphql-yoga点击查看免费下载相关推荐GraphQL Yoga 生态实战基于 Envelop 与 graphql-sse 实现 Server-Sent Events 订阅服务GraphQL Yoga 生态实战基于 Envelop 与 graphql sse 实现 Server Sent Events 订阅服务 本文以仓库中 exa后端API设计从零开始使用graphql-ws构建高性能WebSocket GraphQL服务的完整指南从零开始使用graphql ws构建高性能WebSocket GraphQL服务的完整指南 graphql ws是一个零依赖、轻量级且符合GraphQL ov基于 Prisma 服务构建 GraphQL 服务器使用 graphql-yoga 与 prisma-binding 的完整实战基于 Prisma 服务构建 GraphQL 服务器使用 graphql yoga 与 prisma binding 的完整实战 本文是一篇完整的实战指南以后端数据库GraphQL上一篇Haystack Agent 组件深度解析构建 provider 无关的工具调用智能体与 State 运行时状态下一篇G6 自定义插件开发指南从继承 BasePlugin 到注册与配置创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考