ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Apollo Client 的 Relay 订阅适配器:createFetchMultipartSubscription 深入解析

Apollo Client 的 Relay 订阅适配器:createFetchMultipartSubscription 深入解析 Apollo Client 的 Relay 订阅适配器createFetchMultipartSubscription 深入解析【免费下载链接】apollo-clientThe industry-leading GraphQL client for TypeScript, JavaScript, React, Vue, Angular, and more. Apollo Client delivers powerful caching, intuitive APIs, and comprehensive developer tools to accelerate your app development.项目地址: https://gitcode.com/gh_mirrors/ap/apollo-client本篇文章以 Apollo Client 仓库中的 API 报告 .api-reports/api-report-utilities_subscriptions_relay.api.md 为主线系统讲解apollo/client/utilities/subscriptions/relay子路径下createFetchMultipartSubscription的完整 API 签名、参数语义、内部实现原理与在 Relay 应用中的接入方式。读完本文你将能够在 Relay 环境中利用 Apollo Client 的 HTTP 多部分multipart增量响应解析能力搭建订阅网络层并理解其取消机制与错误处理边界。一、背景为什么需要 Relay 网络层适配器GraphQL 规范并未规定订阅操作的传输协议。Apollo Client 在普通 Apollo 应用中通过HttpLink原生支持基于 HTTP 的分块 multipart 响应订阅详见 docs/source/data/subscriptions.mdx 的 HTTP 小节无需额外配置即可自动发送所需头部。但当应用本身基于 Relay 生态使用relay-runtime的Environment、Network时Relay 的网络层接口与 Apollo 的ApolloLink并不兼容——Relay 要求网络层提供一个形如(RequestParameters, Variables) ObservableGraphQLResponse的订阅执行函数。为此Apollo Client 提供了面向 Relay 的网络层适配器负责复用其 HTTP multipart 响应解析管线这正是 API 报告所描述的createFetchMultipartSubscription的用途。从仓库的包导出配置 package.json 可以看到该模块以独立子路径对外暴露./utilities/subscriptions/relay: ./src/utilities/subscriptions/relay/index.ts二、API 签名与类型定义API 报告给出了该模块的核心导出与相关类型// public export function createFetchMultipartSubscription( uri: string, { fetch: preferredFetch, headers }?: CreateMultipartSubscriptionOptions ): ( operation: RequestParameters, variables: OperationVariables ) ObservableGraphQLResponse; type CreateMultipartSubscriptionOptions { fetch?: WindowOrWorkerGlobalScope[fetch]; headers?: Recordstring, string; };三个要点值得注意uri: string订阅请求发送的目标 GraphQL HTTP 端点地址。可选配置对象支持注入自定义fetch实现例如用于 Node 环境或测试 mock以及额外请求头headers。报告同时标注了ae-forgotten-export警告说明CreateMultipartSubscriptionOptions属于未从入口类型文件导出的内部类型——它在实际使用中通常以内联对象字面量形式传入不会造成问题。返回值一个符合 RelayNetwork要求的订阅执行函数其参数分别是relay-runtime的RequestParameters来自relay-runtime和apollo/client的OperationVariables返回relay-runtime的ObservableGraphQLResponse。与 Relay 的集成示例官方文档 docs/source/data/subscriptions.mdx 给出了完整接入示例将适配器作为Network.create的第三个参数注入 Relay 环境import { createFetchMultipartSubscription } from apollo/client/utilities/subscriptions/relay; import { Environment, Network, RecordSource, Store } from relay-runtime; const fetchMultipartSubs createFetchMultipartSubscription( https://api.example.com ); const network Network.create(fetchQuery, fetchMultipartSubs); export const RelayEnvironment new Environment({ network, store: new Store(new RecordSource()), });这里fetchQuery是你的查询/变更执行函数fetchMultipartSubs则专职处理订阅操作。三、内部实现原理适配器的核心实现位于 src/utilities/subscriptions/relay/index.ts。整体流程可分为四个阶段。1. 构造请求体与默认请求选项const body: BaseHttpLink.Body { operationName: operation.name, variables, query: operation.text || , }; const options generateOptionsForMultipartSubscription(headers || {});请求体由 Relay 的RequestParameters映射而来operationName取自operation.namequery取自operation.text若文本为空则回落为空字符串variables为调用方传入的变量对象。随后通过内部辅助函数generateOptionsForMultipartSubscription生成请求选项其逻辑源码同文件 L74-L87如下function generateOptionsForMultipartSubscription( headers: Recordstring, string ) { const options { ...fallbackHttpConfig.options, headers: { ...(headers || {}), ...fallbackHttpConfig.headers, accept: multipart/mixed;boundarygraphql;subscriptionSpec1.0,application/json, }, }; return options; }fallbackHttpConfig来自 Apollo HTTP link 的公共配置 src/link/http/selectHttpOptionsAndBody.ts其中请求方法默认为POSTdefaultOptions默认头包含accept: application/graphql-responsejson,application/json;q0.9与content-type: application/json同文件 L19-L34。适配器在合并用户传入的headers之后会用multipart/mixed;boundarygraphql;subscriptionSpec1.0,application/json覆盖accept头明确告知服务器客户端期望 multipart 增量投递同时也接受application/json作为普通响应回落。这与其他 Apollo HTTP link 对 multipart 请求的处理保持一致。2. JSON 序列化与 fetch 发起try { options.body JSON.stringify(body); } catch (parseError) { sink.error(parseError as Error); return; }请求体先进行JSON.stringify。若变量对象存在循环引用等无法序列化的情况会直接向订阅sink抛出错误并终止——测试 src/utilities/subscriptions/relay/tests/createFetchMultipartSubscription.test.ts 专门验证了JSON 序列化失败时不调用 fetch这一行为。随后选择实际的 fetch 实现优先使用用户注入的preferredFetch否则回退到全局fetch通过maybe(() fetch)的防御性包装访问并将AbortController的signal一并传入const currentFetch preferredFetch || maybe(() fetch) || backupFetch; currentFetch!(uri, { ...options, signal: controller.signal })3. multipart 响应解析收到响应后适配器检查响应的content-typeconst ctype response.headers?.get(content-type); if (ctype ! null /^multipart\/mixed/i.test(ctype)) { return readMultipartBody(response, observerNext); } sink.error(new Error(Expected multipart response));若响应为multipart/mixed则委托给从 HTTP link 内部导入的readMultipartBody进行流式解析否则视为协议不符向sink抛出Expected multipart response错误。readMultipartBody定义于 src/link/http/parseAndCheckHttpResponse.ts是 Apollo HTTP link 共用的解析管线它逐块消费 multipart body对每个分块执行 JSON 解析跳过空对象对于 Apollo 格式的 payload 结果{ payload, errors? }会将errors包装为CombinedProtocolErrors存入extensions后逐块推送给订阅者从而以增量方式向 Relay 的Observable投递GraphQLResponse。这意味着服务器端渐进式交付如defer、流式增量的每个 payload 都能实时送达订阅消费者。4. 完成与取消AbortController 语义.then(() { sink.complete(); }) .catch((err: any) { if (err.name ! AbortError) { sink.error(err); } }); return () { controller.abort(); };解析完成后调用sink.complete()正常结束订阅任何非AbortError的错误都会通过sink.error抛出订阅者调用unsubscribe()时清理函数触发controller.abort()从而中止底层 fetch 请求。测试文件 createFetchMultipartSubscription.test.ts 对这套取消机制做了完整覆盖可作为行为契约参考signal 传递fetch被调用时必带AbortSignal且初始aborted falseL16-L42取消传播unsubscribe()后信号变为aborted trueL44-L71AbortError 静默因取消而导致的AbortError不会触发sink.error避免正常取消被当成异常L73-L104非中止错误照常上报普通的网络失败如Network failure仍会调用sink.errorL106-L128。四、配置参数详解CreateMultipartSubscriptionOptions两个可选字段的语义归纳如下参数类型默认行为说明fetchWindowOrWorkerGlobalScope[fetch]全局fetch按需兜底自定义 fetch 实现适用于 Node.js 服务端渲染、单元测试 mock 或带超时/重试的封装 fetchheadersRecordstring, string空对象附加请求头会与默认头合并accept头由适配器强制设为 multipart 值不可被覆盖从源码结构看headers的合并顺序为用户头在前、默认头在后、accept最后强制写入因此用户传入的accept会被 multipart 值覆盖而content-type: application/json与POST方法由fallbackHttpConfig提供与 Apollo HTTP link 的请求构造完全同源。五、适用前提与使用限制服务器必须支持 multipart 订阅协议适配器仅解析multipart/mixed响应若端点返回普通 JSON 响应会以Expected multipart response报错因此请先确认 GraphQL 端点支持增量投递如 graphql-over-http 的 IncrementalDelivery 规范。面向 Relay 运行时函数的入参出参均为relay-runtime类型RequestParameters、ObservableGraphQLResponse仅适用于 Relay 的Network.create订阅参数位不能直接作为 Apollo 的 link 使用。解析管线复用 HTTP link 实现readMultipartBody、fallbackHttpConfig均从src/link/http/内部导入意味着该适配器与 Apollo HTTP link 的增量响应处理、错误类型ServerError、ServerParseError、CombinedProtocolErrors行为保持一致便于在混合技术栈中维持一致的订阅语义。六、小结createFetchMultipartSubscription是 Apollo Client 为 Relay 生态提供的轻量网络层适配器它以极小的 API 表面一个uri参数 两个可选配置项复用了 Apollo 成熟的 HTTP multipart 解析管线为 Relay 应用带来与原生 Apollo 一致的增量订阅体验并通过AbortController实现了规范的取消语义。相关源码src/utilities/subscriptions/relay/index.ts、行为测试src/utilities/subscriptions/relay/tests/createFetchMultipartSubscription.test.ts与使用文档docs/source/data/subscriptions.mdx均在仓库中可查是理解并落地该能力的完整参考。【免费下载链接】apollo-clientThe industry-leading GraphQL client for TypeScript, JavaScript, React, Vue, Angular, and more. Apollo Client delivers powerful caching, intuitive APIs, and comprehensive developer tools to accelerate your app development.项目地址: https://gitcode.com/gh_mirrors/ap/apollo-client创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进