news 2026/9/25 4:20:50

graphql-yoga:基于 Envelop 与 Socket.IO 实现 GraphQL 实时通信通道示例详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
graphql-yoga:基于 Envelop 与 Socket.IO 实现 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-socket.io 示例为核心,讲解如何在不使用 Yoga HTTP 入口的情况下,把@envelop/core的插件化执行管线(parse/validate/execute/subscribe + 插件体系)接入一个既有的 Socket.IO 服务器,从而获得 GraphQL 的实时/长连接执行能力。读完本文,你能完整复现该示例的运行流程,并理解 Envelop 的envelop()工厂如何为每个操作(operation)按需产出执行函数,以及useSchema、useLogger两个插件在管线中的具体挂载点。

示例定位与运行方式

该示例的主题是:用 Envelop 实现 GraphQL 流程,并借助@n1ru4l/socket-io-graphql-server库为现有 Socket.IO 服务器增强 GraphQL 能力。它属于examples/envelop/目录下一组“Envelop 跨传输层集成”示例之一(同目录还有 apollo-server、graphql-ws、graphql-sse、graphql-socket.io 等),专门演示 Envelop 作为执行编排核心、传输协议可插拔的设计。

README 给出的运行步骤完整如下,可直接照做:

  1. 在仓库根目录安装全部依赖(使用pnpm);
  2. cd进入examples/envelop/graphql-socket.io目录,运行pnpm run start;
  3. 运行pnpm run test:client,对服务器执行一次测试操作。

对照 package.json 中的 scripts 定义,这两条命令的实际含义是:

"scripts": { "start": "ts-node index.ts", // 直接以 ts-node 启动服务端 "test:client": "ts-node test-client.ts" // 直接以 ts-node 启动客户端 }

即无需任何编译步骤,ts-node直接解释执行 TypeScript 源文件。服务端依赖中的关键版本(以当前仓库为准):socket.io4.8.3、socket.io-client4.8.3、@n1ru4l/socket-io-graphql-server与@n1ru4l/socket-io-graphql-client均为 0.13.0、graphql17.0.2、@graphql-tools/schema10.0.31;其中@envelop/core使用"*"表示走本仓库 workspace 内源码(package.json 中engines要求 Node>=18.0.0,运行本示例的最低环境即由此确定)。

服务端实现逐段解析

完整的服务器代码见 index.ts,共 55 行,可以分成四个环节:

import * as http from 'http'; import { execute, parse, subscribe, validate } from 'graphql'; import { Server } from 'socket.io'; import { envelop, useLogger, useSchema } from '@envelop/core'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { registerSocketIOGraphQLServer } from '@n1ru4l/socket-io-graphql-server'; // 1. 定义可执行 schema const schema = makeExecutableSchema({ typeDefs: /* GraphQL */ ` type Query { hello: String! } `, resolvers: { Query: { hello: () => 'World', }, }, }); // 2. 创建 Envelop 执行管线 const getEnveloped = envelop({ parse, validate, execute, subscribe, plugins: [useSchema(schema), useLogger()], }); // 3. 创建 HTTP + Socket.IO 服务器 const httpServer = http.createServer(); const socketServer = new Server(httpServer); // 4. 将 GraphQL 能力注册到 Socket.IO 服务器 registerSocketIOGraphQLServer({ socketServer, getParameter: async ({ socket, graphQLPayload }) => { const { schema, contextFactory, parse, validate, execute, subscribe } = getEnveloped({ socket, graphQLPayload, }); return { parse, validate, execute, subscribe, graphQLExecutionParameter: { schema, contextValue: await contextFactory(), }, }; }, }); httpServer.listen(3000, () => { // eslint-disable-next-line no-console console.log('Listening on http://localhost:3000'); });

schema 与 Envelop 管线的组装

schema 由@graphql-tools/schema的makeExecutableSchema构造,包含一个最简Query.hello字段。envelop()工厂接收parse/validate/execute/subscribe四个 graphql-js 基础函数(作为管线默认实现)以及plugins数组,返回一个getEnveloped函数。

从源码看(create.ts),envelop()内部先创建 orchestrator 对插件做编排,随后返回的getEnveloped(context)在每次调用时以传入的初始 context 为种子,产出一组被 instrumentation 包裹的执行函数:

return { parse: instrumented.fn(instrumentation?.parse, typedOrchestrator.parse(context)), validate: instrumented.fn(instrumentation?.validate, typedOrchestrator.validate(context)), contextFactory: instrumented.fn(instrumentation?.context, typedOrchestrator.contextFactory(context as any)), execute: instrumented.asyncFn(instrumentation?.execute, typedOrchestrator.execute), subscribe: instrumented.asyncFn(instrumentation?.subscribe, typedOrchestrator.subscribe), schema: typedOrchestrator.getCurrentSchema(), };

这解释了示例中getEnveloped({ socket, graphQLPayload })的写法:每次收到一个 GraphQL 操作,都调用一次getEnveloped并带上操作特有的信息(Socket.IO 的socket对象与原始graphQLPayload)作为初始 context,得到该操作私有的parse/validate/execute/subscribe与schema。context 的初始化即从这里开始——这正是把“连接级信息”(如 socket)带入执行上下文的入口点。

两个插件的挂载点

plugins: [useSchema(schema), useLogger()]分别做两件事,均可在 packages/envelop/core/src/plugins 下核对实现:

  • useSchema(schema)(use-schema.ts):在onPluginInit钩子中调用setSchema(schema),把 schema 注入编排器,使getEnveloped()返回的schema字段(orchestrator 的getCurrentSchema())以及后续解析、校验都能拿到同一份 schema。
  • useLogger()(use-logger.ts):挂在onExecute/onSubscribe钩子上,执行时输出execute-start/execute-end,订阅时输出subscribe-start/subscribe-end(含args与结果result)。它支持skipIntrospection选项,为 true 时在onParse阶段检测 introspection 操作并跳过日志。因此运行test:client时,服务端终端会打印出本次查询的执行出入参——这也是验证“操作确实走过了 Envelop 管线”的直观证据。

getParameter:Envelop 与 Socket.IO 协议的交接点

registerSocketIOGraphQLServer是外部库@n1ru4l/socket-io-graphql-server提供的注册函数,其核心入参是getParameter回调:每当 Socket.IO 客户端发起一次 GraphQL 操作,该回调就会收到{ socket, graphQLPayload },并需要返回:

  • parse/validate/execute/subscribe:由getEnveloped(...)产出的、经过全部插件增强的执行函数;
  • graphQLExecutionParameter:包含schema与contextValue: await contextFactory()——注意contextFactory()是异步函数,这里await其结果得到完整的执行上下文值。

从示例结构可以看出 Envelop 的通用集成模式:传输层(Socket.IO)只负责协议的收发与生命周期,Envelop 负责把一次操作转化为“解析 → 校验 → 执行/订阅”的可编排管线;getParameter就是二者的交接点,任何能在该回调中拿到请求/操作信息的传输层,都可以按同样方式接入。

客户端测试脚本

pnpm run test:client执行的 test-client.ts 完整代码如下:

import { io } from 'socket.io-client'; import { createSocketIOGraphQLClient } from '@n1ru4l/socket-io-graphql-client'; const socket = io('http://localhost:3000'); const client = createSocketIOGraphQLClient(socket); socket.on('connect', async () => { const execution = client.execute({ operation: /* GraphQL */ ` query { hello } `, }); for await (const result of execution) { // eslint-disable-next-line no-console console.log(JSON.stringify(result)); } client.destroy(); socket.close(); });

其流程为:用socket.io-client连接http://localhost:3000(即服务端httpServer.listen(3000)的端口)→ 用createSocketIOGraphQLClient(socket)把该 socket 包装为 GraphQL 客户端 → 在connect事件中调用client.execute({ operation })发起query { hello }查询 →client.execute返回一个可异步迭代的执行流,用for await...of逐条消费结果(该写法同样兼容订阅类操作的多帧结果)→ 结束后client.destroy()与socket.close()释放连接。成功运行后,客户端终端会打印服务端 schema 返回的查询结果 JSON,服务端终端则会打印useLogger的execute-start/execute-end日志。

关键要点小结

  • 本示例证明 Envelop(@envelop/core)不绑定任何特定 HTTP 框架或传输层:示例目录examples/envelop/下并列的 apollo-server、graphql-ws、graphql-sse、graphql-socket.io 等示例,展示了同一套envelop()+ 插件机制接入不同传输协议的方式;
  • envelop()返回的getEnveloped是“按操作实例化”的执行函数工厂(见 create.ts),在 Socket.IO 场景中通过getParameter回调把 socket 级信息注入初始 context;
  • useSchema通过onPluginInit注入 schema,useLogger通过onExecute/onSubscribe提供执行可观测性,两者都是插件钩子体系的直接体现;
  • 运行前提:仓库根目录pnpm install、Node >= 18、按 README 顺序执行pnpm run start与pnpm run test:client;示例包为private: true,仅用于学习参考,不作为 npm 包发布。
  • 后端
  • 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
点击查看免费下载
上一篇:7个实用技巧:掌握rr调试框架中的录制与回放核心功能
下一篇:DeiT3_small_patch16_224.fb_in1k vs 传统CNN:为什么Transformer是图像分类的未来?🚀

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 4:18:46

5G SA现网优化实战:信令三层定位与参数联动调优

简介:本资源是面向5G网络优化工程师及通信运维人员的实战型技术指导手册,聚焦SA架构下用户低接入率问题的系统性分析与优化。内容覆盖无线接通率三大核心指标(RRC建立成功率、QoS Flow建立成功率、NG信令连接成功率)的定义、根因定…

作者头像 李华
网站建设 2026/9/25 4:18:42

Windows域控制器部署实战:DNS集成、时间同步与生产级配置

1. 为什么必须亲手部署一台域控制器——不是“能用就行”,而是“必须稳如磐石”在企业IT基础设施里,“域”不是个可有可无的装饰品,它是整个Windows环境的身份中枢、策略引擎和信任基石。我见过太多场景:新来的运维同事照着某篇博…

作者头像 李华
网站建设 2026/9/25 4:17:15

国奖AI竞赛源码拆解:从工程骨架到答辩闭环的通用模板

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:17:10

GPS Android底层驱动全链路解析:从NMEA数据到HAL/JNI回调

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:17:01

Context Mode 实战:用 SQLite FTS5 与 MCP 构建上下文窗口管理骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华