- 后端
- 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.
本指南以仓库中 examples/envelop/graphql-helix/README.md 为核心,结合 index.ts 源码与 @envelop/core 的插件文档,完整讲解 Envelop 与 GraphQL-Helix 的集成方式:从搭建最小可运行服务器、理解 GraphQL 执行管线(parse/validate/execute/subscribe),到接入内置插件(useSchema、useLogger)并扩展多种传输层与认证等真实场景,最终掌握一套可插拔、与 HTTP 框架解耦的 GraphQL 服务器架构。
一、示例概览:Envelop 与 GraphQL-Helix 扮演什么角色
该示例演示的是:用 Envelop 编排 GraphQL 执行流程,用 GraphQL-Helix 抽象 HTTP 层执行,两者配合实现一个不绑定特定 HTTP 框架的 GraphQL 服务器。
- Envelop(
@envelop/core):提供统一的 GraphQL 执行编排层,通过插件系统将解析(parse)、校验(validate)、执行(execute)、订阅(subscribe)等阶段串联起来,并在每个阶段注入可插拔能力。 - GraphQL-Helix:负责把 HTTP 请求"翻译"成 GraphQL 执行所需的标准输入(operationName、query、variables),并负责把执行结果写回响应——它不关心你用的是 Fastify、Express 还是原生
http,因此天然支持多种传输层(HTTP GET/POST、WebSocket 等)。
示例的完整流程可以概括为:客户端请求 → GraphQL-Helix 解析请求参数 → Envelop 编排执行(经插件管线)→ 结果写回响应。
与其他示例的关系
仓库examples/envelop目录下还提供了大量基于同一思路的变体,例如:
- graphql-helix-auth0:在本示例基础上叠加 Auth0 认证;
- graphql-helix-defer-stream:叠加
@defer/@stream支持; - 以及 apollo-server、express-graphql、graphql-ws 等基于其他执行器/传输层的对照实现。
这组示例共同说明:Envelop 的核心价值在于"与传输层解耦"——无论底层 HTTP 框架如何变化,GraphQL 的执行逻辑都由 Envelop 统一编排。
二、运行示例:三步启动一个可查询的 GraphQL 服务器
按 README 的说明,运行步骤如下:
- 安装依赖:在仓库根目录使用
pnpm安装全部依赖(本仓库使用 pnpm workspace 管理多包依赖,见根目录 pnpm-workspace.yaml); - 启动示例:进入示例目录并启动:
cd examples/envelop/graphql-helix pnpm run startpackage.json中定义的启动脚本为ts-node index.ts,即直接用 ts-node 运行 TypeScript 源码(见 package.json); - 发起查询:浏览器打开
http://localhost:3000/graphql,执行query { hello },即可看到解析器返回"World"。
版本前提:示例依赖
graphql@17.0.2、fastify@5.8.5、graphql-helix@1.13.0与@envelop/core(当前为 5.x),并需要 Node.js 环境支持 ts-node 运行 TypeScript(见 package.json)。依赖锁定关系可查看根目录 pnpm-lock.yaml。
三、核心源码拆解:从 HTTP 请求到 GraphQL 响应的完整调用链
以下是 index.ts 的完整流程拆解,分四个层次理解。
3.1 定义 Schema 与解析器
示例使用@graphql-tools/schema的makeExecutableSchema构造 schema,定义了一个最简单的Query.hello字段:
const schema = makeExecutableSchema({ typeDefs: /* GraphQL */ ` type Query { hello: String! } `, resolvers: { Query: { hello: () => 'World', }, }, });这个 schema 对象会被作为 Envelop 的初始 schema 提供给整个执行管线。
3.2 用 Envelop 组装执行管线
这是整个示例的核心,把 GraphQL 官方的四个执行阶段全部交给 Envelop 编排:
const getEnveloped = envelop({ parse, validate, execute, subscribe, plugins: [useSchema(schema), useLogger()], });从 @envelop/core 的 create.ts 源码可见,envelop()接收{ plugins, enableInternalTracing? }选项,返回一个getEnveloped函数。每次调用getEnveloped(context),都会得到一组经过插件管线包装的执行能力:
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)), execute: instrumented.asyncFn(instrumentation?.execute, typedOrchestrator.execute), subscribe: instrumented.asyncFn(instrumentation?.subscribe, typedOrchestrator.subscribe), schema: typedOrchestrator.getCurrentSchema(), };也就是说,getEnveloped返回的parse/validate/execute/subscribe并不是graphql包里的原始函数,而是经 orchestrator 串联各插件后的增强版本,且每个请求调用一次,从而能为每个请求构建独立的 context。
实现细节:Envelop 的
envelop()与getEnveloped()分工如下——前者在服务启动时一次性创建 orchestrator 与 instrumentation,后者在每个请求到来时按当前 context 组装本请求专用的执行函数(详见 create.ts 与 orchestrator.ts)。
3.3 用 GraphQL-Helix 桥接 HTTP 层
示例基于 Fastify 注册了一个同时接受GET与POST的路由,核心是"三件套":
app.route({ method: ['GET', 'POST'], url: '/graphql', async handler(req, res) { const { parse, validate, contextFactory, execute, schema } = getEnveloped({ req }); const request = { body: req.body, headers: req.headers, method: req.method, query: req.query, }; if (shouldRenderGraphiQL(request)) { res.type('text/html'); res.send(renderGraphiQL({})); } else { const { operationName, query, variables } = getGraphQLParameters(request); const result = await processRequest({ operationName, query, variables, request, schema, parse, validate, execute, contextFactory, }); sendResult(result, res.raw); res.sent = true; } }, });各环节职责如下:
| 函数/参数 | 作用 |
|---|---|
getEnveloped({ req }) | 为当前请求构建 Envelop 执行管线,传入的req会成为插件 context 的初始来源(供useLogger、useAuth0等插件读取请求信息) |
shouldRenderGraphiQL(request) | 判断当前请求是否应返回 GraphiQL 交互界面(浏览器GET请求通常触发) |
renderGraphiQL({}) | 渲染 GraphiQL HTML 页面 |
getGraphQLParameters(request) | 从 HTTP 请求中提取operationName、query、variables三个标准参数 |
processRequest({ ... }) | GraphQL-Helix 的核心:接收已提取的参数 + Envelop 提供的执行函数,完成实际执行并返回传输无关的结果 |
sendResult(result, res.raw) | 将结果以text/event-stream(SSE)或 JSON 形式写回底层响应对象(这里传入的是 Fastify 的原生res.raw) |
res.sent = true | 告知 Fastify 响应已由sendResult直接写入,避免框架重复发送 |
值得注意:request对象被构造成{ body, headers, method, query }的与框架无关的形态,这正是 GraphQL-Helix 的抽象方式——它只依赖 WHATWG 风格的请求结构,因此同一套代码可以移植到 Express、Koa 等其他框架。
3.4 生命周期:为什么"每个请求调用一次 getEnveloped"
getEnveloped是在路由 handler内部调用的,这并非巧合,而是 Envelop 按请求构建 context 的设计使然:
- 服务启动时:
envelop()只做一次,创建 orchestrator; - 每次请求时:
getEnveloped({ req })重新执行,为本次请求生成独立的contextFactory、execute等函数; - 插件可以在
contextFactory阶段把req信息(如请求头、认证信息)注入 GraphQL context,供解析器与后续插件使用。
这也意味着所有 Envelop 插件都能感知到当前 HTTP 请求,为认证、日志、限流等横切能力提供了统一的挂载点。
四、插件体系:useSchema与useLogger的底层机制
示例启用了useSchema(schema)与useLogger()两个内置插件。它们均来自@envelop/core,官方文档位于 packages/envelop/core/docs。
4.1useSchema:为管线提供 GraphQL Schema
根据 use-schema.md,这是指定 GraphQL schema 的最简插件,任何能产出GraphQLSchema对象的工具(如buildSchema、makeExecutableSchema、GraphQL Modules、Pothos 等)都能接入:
import { envelop, useEngine, useSchema } from '@envelop/core'; const mySchema = buildSchema(/* ... */); const getEnveloped = envelop({ plugins: [ useEngine({ parse, validate, specifiedRules, execute, subscribe }), useSchema(mySchema), // ... 其他插件 ], });@envelop/core还提供了useSchemaByContext(按 context 动态选择 schema)、useMaskedErrors(屏蔽错误详情)、useExtendContext(扩展 context)、useErrorHandler、usePayloadFormatter、useValidationRule等内置插件,完整清单见 @envelop/core README。
4.2useLogger:记录各执行阶段的事件
根据 use-logger.md,useLogger会记录执行各阶段(parse、validate、context、execute、subscribe 等)的参数与信息,且支持自定义日志函数:
import { envelop, useLogger } from '@envelop/core'; const getEnveloped = envelop({ plugins: [ useLogger({ logFn: (eventName, args) => { // eventName 取值示例: // 'execute-start' / 'execute-end' / 'subscribe-start' / 'subscribe-end' // 'start' 事件时 args 包含传给 execute/subscribe 的参数; // 'end' 事件时 args 额外包含执行结果。 }, }), // ... 其他插件 ], });这意味着你可以把useLogger接到自己的日志体系(如 pino、winston),并可在execute-end中记录执行耗时、错误信息等,实现可观测性。
4.3 插件化带来什么
由于执行管线完全由插件编排,以下能力可以以"声明式插件"的方式叠加,而无需修改 GraphQL 执行逻辑:
- 认证:如 graphql-helix-auth0 所示,只需在插件数组中追加
useAuth0(...),并注册useSchema,即可把 Auth0 的认证信息(如sub)注入 context 供解析器使用; - 日志:
useLogger; - 错误处理:
useMaskedErrors、useErrorHandler; - 校验规则扩展:
useValidationRule; - schema 切换:
useSchemaByContext(多租户场景)。
五、工程实践:基于本示例扩展出更多传输与场景
5.1 从 Fastify 迁移到其他框架
因为 GraphQL-Helix 的processRequest只依赖{ body, headers, method, query }结构,而sendResult接受任意响应对象,换 HTTP 框架只需要改路由注册层。例如把app.route(...)换成 Express 的app.all('/graphql', handler),handler 内构造同样的request对象即可,GraphQL 侧代码几乎不变。
5.2 增加订阅支持
示例中 Envelop 已经传入了subscribe,因此只需在 GraphQL-Helix 侧接入 WebSocket 或 SSE 传输(参考仓库中 graphql-sse 与 graphql-ws 两个示例),即可在同一 schema 上同时支持查询与订阅。
5.3 观察学习路线
- 想了解认证增强:对比阅读 graphql-helix-auth0/index.ts;
- 想了解
@defer/@stream:阅读 graphql-helix-defer-stream; - 想了解 Envelop 完整插件能力:查阅 packages/envelop/core/docs 下的 9 份插件文档,以及 packages/envelop/plugins 目录下
jwt、apq、csrf-prevention、response-cache、prometheus、persisted-operations等企业级插件。
六、小结
本示例用约 70 行代码展示了一个完整、可扩展的 GraphQL 服务器骨架:
- 用 Envelop 统一编排 GraphQL 执行——通过
envelop()+ 插件数组组装管线,getEnveloped(req)按请求注入 context; - 用 GraphQL-Helix 抽象 HTTP——
getGraphQLParameters提取参数、processRequest执行、sendResult回写,与具体 HTTP 框架解耦; - 用内置插件快速获得生产能力——
useSchema注入 schema,useLogger记录执行事件,更多插件按需叠加。
如果你需要一套"与框架无关、插件可插拔、便于在任意 JS 环境部署"的 GraphQL 服务端架构,从 examples/envelop/graphql-helix 开始,是一条非常清晰的入门路径。
- 后端
- 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.
相关推荐
GraphQL Yoga 示例解析:用 Pothos + Envelop + GraphQL Helix 在 Fastify 上构建类型安全 GraphQL 服务
GraphQL Yoga 示例解析:用 Pothos + Envelop + GraphQL Helix 在 Fastify 上构建类型安全 GraphQL 服
后端API设计在 Azure Functions 上使用 Envelop 与 GraphQL-Helix 搭建基础 GraphQL 服务:graphql-yoga 仓库官方示例深度解析
在 Azure Functions 上使用 Envelop 与 GraphQL Helix 搭建基础 GraphQL 服务:graphql yoga 仓库官方示
后端API设计GraphQL Yoga 示例实战:用 Envelop 与 graphql-helix 实现 @defer/@stream 增量交付
GraphQL Yoga 示例实战:用 Envelop 与 graphql helix 实现 @defer/@stream 增量交付 本篇基于 examples
后端API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考