☰
Graffle HTTP 传输 methodMode `getReads` 实战:用 HTTP GET 发送 GraphQL 读操作
2026/10/10 11:50:10 网站建设 项目流程
  • 后端

【免费下载链接】graffle

Simple GraphQL Client for JavaScript. Minimal. Extensible. Type Safe. Runs everywhere.

项目地址:https://gitcode.com/gh_mirrors/gr/graffle
点击查看免费下载

导读

本文围绕 Graffle(Simple GraphQL Client for JavaScript)HTTP 传输中的methodMode: 'getReads'方法模式展开,讲解如何让 query / subscription 等读类型操作通过 HTTP GET 发送、同时让 mutation 写操作继续走 HTTP POST 的完整配置方法。读完本文你将掌握methodMode两种取值(post/getReads)的语义差异、GET 模式下请求参数如何编码进 URL 搜索参数、底层方法选择逻辑,以及如何借助 anyware 中间件验证最终发出的请求。

本文核心素材来自仓库中的 method-get 示例文档、示例源码 及其 输出记录,底层实现参考 TransportHttp 扩展源码 与 HTTP 编码实现。

一、getReads模式解决什么问题

HTTP 传输默认情况下所有请求都通过 HTTP POST 发送(这也是 TransportHttp 配置默认值 中methodMode: 'post'的含义)。但在某些场景下,我们希望读操作(query、subscription)走 HTTP GET,从而获得 GET 的缓存友好、可分享 URL 等特性,同时保持写操作(mutation)走 POST。

Graffle 的methodMode配置提供了两种取值(见 MethodMode 常量定义):

取值行为
post(默认)所有操作均通过 HTTP POST 发送,请求体为 JSON
getReads读类型操作(query、subscription)通过 HTTP GET 发送,写类型操作(mutation)仍通过 HTTP POST 发送

二、最小可运行示例

以下完整示例来自 examples/10_transport-http/transport-http_method-get.ts,它创建一个 Graffle 客户端,配置methodMode: 'getReads',并通过 anyware 中间件打印出每次请求的完整信息:

import { Graffle } from 'graffle' import { publicGraphQLSchemaEndpoints, show } from '../$/helpers.js' const graffle = Graffle .create() .transport({ url: publicGraphQLSchemaEndpoints.Pokemon, methodMode: `getReads`, // [!code highlight] headers: { tenant: `nano` }, }) .anyware(async ({ exchange }) => { show(exchange.input.request) return await exchange() }) // The following request will use an HTTP POST method because it is // using a "mutation" type of operation. await graffle.gql('mutation { addPokemon(attack:0, defense:0, hp:1, name:"Nano", type: grass) { name } }').$send() // The following request will use an HTTP GET method because it // is using a "query" type of operation. await graffle.gql('query { pokemonByName(name: "Nano") { hp } }').$send()

其中publicGraphQLSchemaEndpoints.Pokemon来自 examples/$/helpers.ts,默认指向http://localhost:3000/graphql,也可以通过环境变量POKEMON_SCHEMA_URL覆盖。

示例中.anyware()的作用是在请求发出前拦截并打印exchange.input.request(即最终构造的 Request 对象),这是验证方法模式是否生效的最直接手段。

三、输出验证:mutation 走 POST,query 走 GET

示例运行后产生的实际输出保存在 examples/outputs/10_transport-http/transport-http_method-get.output.txt,两份输出清晰地展示了两种请求的差异。

第一份输出对应 mutation(addPokemon),虽然methodMode是getReads,但请求仍然走 POST,且请求体为 JSON:

{ methodMode: 'getReads', headers: Headers { accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', 'content-type': 'application/json', tenant: 'nano' }, method: 'post', url: { _tag: 'url', value: URL { href: 'http://localhost:3000/graphql', ... } }, body: '{"query":"mutation { addPokemon(attack:0, defense:0, hp:1, name:\\"Nano\\", type: grass) { name } }"}' }

第二份输出对应 query(pokemonByName),请求走 GET,且 GraphQL 文档被编码进 URL 搜索参数query:

{ methodMode: 'getReads', headers: Headers { accept: 'application/graphql-response+json; charset=utf-8, application/json; charset=utf-8', tenant: 'nano' }, method: 'get', url: { _tag: 'url', value: URL { href: 'http://localhost:3000/graphql?query=query+%7B+pokemonByName%28name%3A+%22Nano%22%29+%7B+hp+%7D+%7D', ... searchParams: URLSearchParams { 'query' => 'query { pokemonByName(name: "Nano") { hp } }' }, } } }

从两份输出可以总结出 GET / POST 请求的差异:

  • method 字段:mutation 为post,query 为get;
  • 请求头:POST 请求带accept与content-type: application/json,GET 请求只带accept(没有content-type,因为请求体为空);
  • 载荷位置:POST 请求将 GraphQL 文档放在 JSON body 中,GET 请求将 GraphQL 文档作为query搜索参数拼接到 URL 上;
  • URL 形态:GET 请求的 URL 会带?query=...搜索串,如http://localhost:3000/graphql?query=query+%7B+pokemonByName...。

四、底层原理:方法选择与载荷编码

4.1 方法选择的判定逻辑

getReads并不是简单地"读操作全走 GET",其底层判定逻辑位于 TransportHttp.ts 的 run 函数:

const methodMode = input.transport.methodMode const requestMethod = methodMode === MethodMode.post ? `post` : GraphqlKit.Schema.OperationType.LookupToAccessKind[operationType] === `read` ? `get` : `post`

从源码结构可以看出:

  1. 若methodMode为post,则一律使用 POST;
  2. 若methodMode为getReads,则根据操作的访问类型(AccessKind)判断:读操作(read,即 query / subscription)用 GET,写操作(write,即 mutation)仍用 POST;
  3. operationType从请求的 operation 字段解析而来(Str.is(input.request.operation)判断是字符串还是结构化对象)。

4.2 GET 的搜索参数编码

GET 请求的载荷编码由 getRequestEncodeSearchParameters 实现,它把 GraphQL 请求编码为 URL 搜索参数对象:

export const getRequestEncodeSearchParameters = (request: RequestConfig): Record<string, string> => { return { query: request.query, ...(request.variables ? { variables: JSON.stringify(request.variables) } : {}), ...(request.operationName ? { operationName: request.operationName } : {}), } }

也就是说,GET 模式下支持三个搜索参数:query(GraphQL 文档)、variables(变量,JSON 序列化)、operationName(操作名),仅在存在时才编码。随后这些搜索参数会通过Http.SearchParams.appendAll/appendAllToPath拼接到 URL 上(TransportHttp.ts)。

4.3 POST 的 JSON body 编码

POST 请求的载荷由 postRequestEncodeBody 实现,将query、variables、operationName序列化为 JSON 字符串作为请求体。

4.4 请求头差异

两类请求的默认请求头也由 HTTP 编码实现 区分:

export const postRequestHeadersRec = { accept: ACCEPT_REC, 'content-type': CONTENT_TYPE_REC, } export const getRequestHeadersRec = { accept: ACCEPT_REC, }

其中ACCEPT_REC为application/graphql-response+json; charset=utf-8, application/json; charset=utf-8,CONTENT_TYPE_REC为application/json,均遵循 GraphQL Over HTTP 规范 的媒体类型约定(对应代码注释见 http/__.ts)。这正是示例输出中 GET 请求没有content-type头的原因。

五、methodMode 的三种配置途径与优先级

methodMode与url、headers、raw一样属于 TransportHttp 的配置项(见 ConfigurationInput 定义),可通过三种途径配置(见 TransportHttp 文档):

  1. 构造函数中:.transport({ methodMode: 'getReads', ... });
  2. with方法中:动态切换配置;
  3. 扩展栈中:通过扩展追加配置。

配置优先级从高到低为:扩展栈(后注册的扩展优先)→with配置 → 构造函数配置;其中raw配置优先级高于transport下其他平级属性。

5.1 显式配置 POST

如果需要显式恢复默认的 POST 行为,可以显式声明:

import { Graffle } from 'graffle' Graffle .create() .transport({ methodMode: 'post', })

5.2 显式配置 getReads

import { Graffle } from 'graffle' Graffle .create() .transport({ methodMode: 'getReads', })

六、url 与 headers 配置细节

在getReads模式下,url的配置同样支持多种形态(TransportHttp.ts):

  • 绝对 URL 字符串:https://api.example.com/graphql
  • 相对路径字符串:/api/graphql、./graphql、../graphql(浏览器环境或框架增强 fetch 场景下有用)
  • URL 对象

需要留意的是,Node.js 原生 fetch 不支持相对 URL,相对路径仅在浏览器或提供增强 fetch 的框架(如 SvelteKit)中有效。配置解析在配置期即完成校验(parseURLInput会在 URL 非法时提前抛出错误),避免运行时才暴露问题。

headers采用合并策略(Http.Headers.mergeInitWithStrategyMerge,见 TransportHttp.ts),且若某个 header 被赋空字符串值,会删除先前设置的该 header。

七、注意事项

  • methodMode: 'getReads'只影响 HTTP 方法选择,不会改变操作本身的性质:mutation 永远走 POST,这是 示例文档 与源码共同确认的行为;
  • GET 模式下 GraphQL 文档会完整出现在 URL 中,若文档包含敏感信息,请结合业务场景评估是否适合使用 GET;
  • transport.raw可以直接透传原生fetch配置(如{ mode: 'cors' }),但由于没有护栏,若在raw.method中设置PATCH之类的值会覆盖methodMode的判定结果,产生不符合 GraphQL Over HTTP 规范的请求(TransportHttp 文档 中的 Raw 一节有明确提醒);
  • 示例输出验证方式可复用:通过.anyware()打印exchange.input.request,即可在任意环境中确认方法模式是否按预期生效。

八、延伸阅读

  • TransportHttp 扩展文档:完整的配置说明、相对 URL 用法、raw 配置与 ware 钩子说明;
  • TransportHttp 实现源码:methodMode判定、URL 解析、请求构造的完整逻辑;
  • HTTP 编码与解码实现:GET 搜索参数编码、POST body 编码、请求头与响应解析;
  • method-get 示例输出:示例实际运行产生的请求快照;
  • 同目录下其他 HTTP 传输示例:raw、abort、headers 等,可对照理解 TransportHttp 的完整能力面。
  • 后端

【免费下载链接】graffle

Simple GraphQL Client for JavaScript. Minimal. Extensible. Type Safe. Runs everywhere.

项目地址:https://gitcode.com/gh_mirrors/gr/graffle
点击查看免费下载
上一篇:GeoLibre 实时协作全解析:基于 Cloudflare Durable Object 的会话同步协议、权限模型与自托管部署指南
下一篇:3大技术突破:开源散热控制器如何彻底改变Dell笔记本性能

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询