首页/新闻资讯/正文详情

urql 持久化查询(APQ)与文件上传实战:从 Automatic Persisted Queries 到 GraphQL Multipart

发布时间:2026/9/25 2:17:16 来源:云帆数科 栏目:资讯中心
urql 持久化查询(APQ)与文件上传实战:从 Automatic Persisted Queries 到 GraphQL Multipart
前端【免费下载链接】urqlThe highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.项目地址https://gitcode.com/gh_mirrors/ur/urql点击查看免费下载本文以urql文档《Persistence Uploads》为骨架系统讲解两大进阶能力基于urql/exchange-persisted的自动持久化查询Automatic Persisted Queries如何通过 SHA256 哈希与extensions.persistedQuery实现 CDN 友好缓存与按需注册以及urql/core4原生内置的 GraphQL Multipart 文件上传如何通过FormData序列化自动生效。读完本文你将掌握persistedExchange的全部配置项与自定义哈希方案理解 GET/POST 切换与重试降级机制并能直接在项目中使用File/Blob变量完成文件上传。概览为什么要持久化查询与文件上传GraphQL 请求默认以 POST 发送而绝大多数 CDN 与 HTTP 缓存不会缓存 POST 请求因此每次查询都直达源站。持久化查询Persisted Queries通过以哈希代替完整查询文本让请求体变小、可被 CDN 与 API 层缓存文件上传则让客户端能够通过 GraphQL API 直接提交二进制文件而不必绕道对象存储或独立上传接口。在urql中这两件事的落地方式完全不同持久化查询需要引入urql/exchange-persisted包并按照特定顺序插入exchanges数组文件上传从urql/core4起原生支持零安装、零配置只要variables中出现File或Blob实例请求会自动切换为multipart/form-data。注早期文件上传依赖urql/multipart-fetch-exchange包该包现已弃用功能合入urql/core4见 fetchSource.ts 头部注释。Automatic Persisted Queries 的工作原理自动持久化查询APQ基于非官方的 GraphQL Persisted Queries Spec。其核心流程如下客户端哈希客户端把 GraphQL 查询文本转换为 SHA256 哈希发送哈希而不是完整查询服务端识别若服务端此前见过该查询直接按哈希处理请求流程与常规请求无异Miss 重试注册若服务端不认识该哈希会返回PersistedQueryNotFound错误。此时客户端应改发「完整查询 哈希」的组合服务端据此登记这条查询后续请求即可命中缓存GET 化增强缓存若只发送哈希过的持久化查询GET 请求CDN 就能轻而易举地缓存它们——因为默认情况下大多数缓存不会自动缓存 POST 请求。在urql中persistedExchange负责这一切它位于其他 fetch/subscription 交换器之前通过修改每个操作的extensions对象为 GraphQL 请求附加持久化查询元数据。仓库源码印证从 persistedExchange.ts 的实现可以看到每个符合条件的操作都会经过getPersistedOperation用makeOperation复制操作并标记persistAttempt: true防止重复哈希处理调用哈希函数对stringifyDocument(operation.query)计算结果将结果写入operation.extensions.persistedQuery { version: 1, sha256Hash }version: 1即规范约定的版本号仅当操作是query类型时才改写context.preferGetMethod推动 GET 化。服务端返回后结果处理逻辑 会识别两类错误PersistedQueryNotFound视为一次 Miss在重试操作上标记persistedQuery.miss: true后通过内部retries流重新 forwardPersistedQueryNotSupported说明服务端根本不支持持久化查询此时置supportsPersistedQueries false彻底关闭后续持久化逻辑并删除persistedQuery扩展后重试保证功能平滑降级。如果同一操作出现两次 Miss开发环境下会打印警告提示可能是ssrExchange等带缓存的交换器投递了过期错误结果建议将persistedExchange移动到fetchExchange之前源码警告信息。安装与基础配置首先安装urql/exchange-persistedyarn add urql/exchange-persisted # 或 npm install --save urql/exchange-persisted然后将persistedExchange加入exchanges数组位置必须放在与 API 通信的交换器如fetchExchange、subscriptionExchange之前import { Client, fetchExchange, cacheExchange } from urql; import { persistedExchange } from urql/exchange-persisted; const client new Client({ url: http://localhost:1234/graphql, exchanges: [ cacheExchange, persistedExchange({ preferGetForPersistedQueries: true, }), fetchExchange, ], });preferGetForPersistedQueriesGET 与 POST 的策略切换这是最常用的配置项推荐设为true让持久化查询走 GET 请求从而让 CDN 发挥作用。true或within-url-limit当拼接后的 URL不超过 2048 字符时使用 GET这也是源码中的默认值见 persistedExchange.ts 的within-url-limit默认分支force强制所有持久化查询使用 GET即使 URL 超过长度限制此时查询文本可能被截断需谨慎使用false或undefined保持 POST。源码中该值最终被写入operation.context.preferGetMethodpersistedExchange.tsfetchExchange读取后会在 GET 模式下省略请求体中的query字段。subscriptionExchange同样理解这些修改如果你用订阅通道承载查询也能协同工作。与其他交换器的协作persistedExchange本身不发起网络请求它只负责改写操作与处理错误重试。fetchExchange看到extensions.persistedQuery后会按需从请求中剔除query。这也是为什么必须把它放在cacheExchange之后、fetchExchange之前——缓存层不应缓存到带有哈希扩展的中间态。仓库中的 with-apq 示例 演示了在客户端开启持久化查询后配合useQuery的正常用法读者可对照vite.config.js与package.json直接运行体验。自定义哈希从 Web Crypto 到编译期哈希persistedExchange默认使用 SHA256 生成哈希。其降级链路在 sha256.ts 中清晰可见浏览器环境优先使用内置Web Crypto APIwindow.crypto.subtle.digestNode.js 环境回退到Node Crypto 模块crypto.createHash(sha256)通过间接require/import加载以避免打包副作用两者都不可用时返回空字符串开发环境打印警告。通过generateHash选项可以完全替换这套逻辑persistedExchange({ generateHash: (_, document) document.documentId, });上面的写法适合配合Webpack 的graphql-persisted-document-loader使用哈希在编译期就已由 loader 生成并写入document.documentId运行时只需直接取用generateHash的第二个参数GraphQLDocumentNode对象即可省去运行时哈希开销。React Native 场景React Native 中没有 Web Crypto API因此必须提供自定义的 SHA256 实现。此时利用generateHash的第一个参数——GraphQL 查询字符串import sha256 from hash.js/lib/hash/sha/256; persistedExchange({ async generateHash(query) { return sha256().update(query).digest(hex); }, });注意generateHash若返回null或undefined该操作将不被当作持久化操作处理即跳过本交换器的逻辑见 PersistedExchangeOptions 注释。这可以用于按操作动态决定是否持久化。非自动模式enforcePersistedQueries如果 API 只接受预注册的持久化查询、拒绝任意查询常见于 API 混淆/加固场景可以关闭 APQ 的重试逻辑persistedExchange({ enforcePersistedQueries: true, });启用后交换器会忽略PersistedQueryNotFound与PersistedQueryNotSupported错误假设所有持久化查询都已注册直接把哈希请求当作常规 GraphQL 请求处理源码中enforcePersistedQueries会跳过重试分支。作用于 mutation 与 subscription默认情况下persistedExchange只处理query操作。若需对变更和订阅启用持久化常用于 API 混淆场景可开启persistedExchange({ enableForMutation: true, enableForSubscriptions: true, });源码中的 operationFilter 按此开关决定哪些kind进入持久化流程。注意preferGetMethod仅对query生效mutation 即便持久化也仍走 POST。File Uploadsurql/core4 的原生文件上传GraphQL 服务端常通过 GraphQL Multipart Request Spec 支持文件上传。urql的用法极其简单在variables中直接传入File或Blob对象在 GraphQL 文档中为对应变量声明标量通常叫File或Upload浏览器中通常通过文件输入控件input typefile拿到File对象。无需任何安装与配置——urql/core4原生支持。当urql在variables任意位置检测到File/Blob时会自动把请求切换为multipart/form-data按规范构建FormData并发送。仓库源码印证extractFiles 与 serializeBody这一能力由 variables.ts 的extractFiles与 fetchOptions.ts 的serializeBody共同实现检测extractFiles递归遍历variables通过instanceof FileConstructor || instanceof BlobConstructor识别文件对象variables.ts并把文件路径记录为variables.xxx.yyy形式的键数组按path.0、path.1索引展开序列化serializeBody发现files.size 0后构造FormDataoperationsJSON 序列化后的操作查询文档 变量文件占位保留mapJSON 化的路径映射{ 0: [variables.file] }0、1、2…按序追加的二进制文件内容。这正是 GraphQL Multipart Request Spec 的规范结构。同时makeFetchOptionsfetchOptions.ts仅在序列化结果是字符串时才设置content-type: application/jsonFormData场景交由 fetch 自动生成multipart/form-data; boundary...头。自定义 File/Blob 的注意事项若你使用自定义版本的File和Blob务必确保它们正确继承原生类才能被instanceof识别为文件。extractFiles只在File/Blob构造函数可用时运行FileConstructor缺省回退为NoopConstructor见 variables.ts。完整示例with-multipart仓库的 with-multipart 示例 提供了可直接运行的上传流程import React, { useState } from react; import { gql, useMutation } from urql; const UPLOAD_FILE gql mutation UploadFile($file: Upload!) { uploadFile(file: $file) { filename } } ; const FileUpload () { const [selectedFile, setSelectedFile] useState(); const [result, uploadFile] useMutation(UPLOAD_FILE); const { data, fetching, error } result; const handleFileUpload () { uploadFile({ file: selectedFile }); }; const handleFileChange event { setSelectedFile(event.target.files[0]); }; return ( div {fetching pLoading.../p} {error pOh no... {error.message}/p} {data data.uploadFile ? ( pFile uploaded to {data.uploadFile.filename}/p ) : ( div input typefile onChange{handleFileChange} / button onClick{handleFileUpload}Upload!/button /div )} /div ); };要点event.target.files[0]取到浏览器File对象后直接放入useMutation的变量{ file: selectedFile }即可其余全部由urql/core4处理。配置项速查表配置项类型默认值说明preferGetForPersistedQueriesboolean \| within-url-limit \| forcewithin-url-limit持久化查询是否使用 GETforce无视 URL 长度强制 GETgenerateHash(query: string, document) Promisestring \| null \| undefined内置 SHA256自定义哈希函数返回空值则跳过持久化enforcePersistedQueriesbooleanfalse启用非自动模式忽略 APQ 错误、禁用重试enableForMutationbooleanfalse对 mutation 操作启用持久化enableForSubscriptionsbooleanfalse对 subscription 操作启用持久化常见问题与排错GET URL 超长within-url-limit模式下超过 2048 字符会退回 POST业务查询特别大时建议用force前先评估 URL 长度与网关限制。两次 Miss 警告开发控制台出现 two misses for the same operation 时优先检查ssrExchange/缓存交换器是否投递了过期错误结果并把persistedExchange移到它们之后fetchExchange之前。React Native 下哈希为空Web Crypto 不可用必须通过generateHash提供实现如hash.js否则操作不会进入持久化流程。上传不生效确认服务端实现了 Multipart Request Spec且变量确实是原生File/Blob实例——自定义的伪文件类不会被instanceof识别。延伸阅读持久化查询交换器文档 与 核心实现、哈希实现 及完整 测试用例文件上传底层fetchSource.tsmultipart/mixed 响应解析、fetchOptions.tsserializeBody、variables.tsextractFiles可直接运行的仓库示例with-apq 与 with-multipart更进阶的缓存主题参见 graphcache 文档上传与持久化的组合可在实际项目中与 retryExchange 等交换器叠加使用赞分享前端【免费下载链接】urqlThe highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.项目地址https://gitcode.com/gh_mirrors/ur/urql点击查看免费下载相关推荐micro-github部署指南用Now.sh一键上线你的GitHub认证服务micro github部署指南用Now.sh一键上线你的GitHub认证服务 micro github是一个轻量级微服务能帮助开发者轻松为应用添加GitH前端Relay 持久化查询Persisted Queries实战指南从 persistConfig 配置到服务端执行Relay 持久化查询Persisted Queries实战指南从 persistConfig 配置到服务端执行 Relay 编译器内置对持久化查询Pe前端开发工具urql 持久化查询实战指南urql/exchange-persisted 的安装、配置与底层原理urql 持久化查询实战指南urql/exchange persisted 的安装、配置与底层原理 urql/exchange persisted 是 u前端上一篇ASN 0.80.2终极网络情报工具10分钟快速上手指南下一篇【亲测免费】 探索生命之树TreeViewer——跨平台的谱系图绘制神器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

IronClaw 网关操作链路的可测性:gateway-traces 确定性回放夹具全解析
IronClaw 网关操作链路的可测性:gateway-traces 确定性回放夹具全解析

人工智能AI 应用交互助手AI Agent 【免费下载链接】ironclaw IronClaw is an Agent OS focused on privacy, security and extensibility 项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw 点击查看 免费下载 本文围绕 IronClaw 仓库中的 tests/fixtures/ga… · 2026/9/25 2:17:16

Transformers 摘要生成(Summarization)微调实战:run_summarization.py 与 run_summarization_no_trainer.py 完整流程解析
Transformers 摘要生成(Summarization)微调实战:run_summarization.py 与 run_summarization_no_trainer.py 完整流程解析

推理引擎大模型 【免费下载链接】FlexGen Running large language models on a single GPU for throughput-oriented scenarios. 项目地址: https://gitcode.com/gh_mirrors/fl/FlexGen 点击查看 免费下载 本文是围绕 HuggingFace Transformers 官方示例中 Summari… · 2026/9/25 2:17:16

GitHub热榜观察:AI智能体与本地生成工具的部署实战
GitHub热榜观察:AI智能体与本地生成工具的部署实战

今天早上刷开 GitHub Trending,大概是有史以来“AI 浓度”最高的一次。排在前面的项目,一眼扫过去基本被两类包圆:一类是 AI 智能体相关的框架、编排工具和案例库,从 agent 工作流到可视化搭建平台都有;另一类是能在本… · 2026/9/25 2:17:16

ClawHub 的 Convex 后端技能体系:主入口 Skill 如何路由到 convex-* 技能生态与在线能力目录
ClawHub 的 Convex 后端技能体系:主入口 Skill 如何路由到 convex-* 技能生态与在线能力目录

后端前端AI 技能AI 插件搜索引擎 【免费下载链接】clawhub Skill Plugin Registry for OpenClaw 项目地址: https://gitcode.com/gh_mirrors/mo/clawhub 点击查看 免费下载 在 OpenClaw 的 Skill Plugin Registry 项目 ClawHub 中,.agents/skills/convex/SKILL.m… · 2026/9/25 2:50:12

山西毅弘探测科技:机载气象传感仪正规源头厂家,用料扎实广受信赖
山西毅弘探测科技:机载气象传感仪正规源头厂家,用料扎实广受信赖

山西毅弘探测科技:机载气象传感仪正规源头厂家,用料扎实广受信赖山西毅弘探测科技有限公司位于龙城太原,是一家从事气象监测设备研发、生产、销售的技术驱动型企业,主营业务涵盖机载式气象仪、便携式气象仪、超声波气象站、压电式… · 2026/9/25 2:50:12

从Anaconda到Miniconda:轻量级Python环境管理实战指南
从Anaconda到Miniconda:轻量级Python环境管理实战指南

/* 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 2:50:12

CAA框架Design_Frame 2.2.1:CATIA刀具设计二次开发实战
CAA框架Design_Frame 2.2.1:CATIA刀具设计二次开发实战

简介:面向CATIA二次开发工程师的刀具设计参数化框架(Design_Frame2.2.1),基于CAA技术构建,用于在CATIA环境中快速搭建刀具参数化建模与知识工程工具。压缩包约6.24MB,共657个文件;核心代码包括4… · 2026/9/25 2:50:12

使用 PaddleSpeech 在 CSMSC 数据集上训练 HiFiGAN 神经声码器:从数据准备到端到端合成实战指南
使用 PaddleSpeech 在 CSMSC 数据集上训练 HiFiGAN 神经声码器:从数据准备到端到端合成实战指南

人工智能语音音频NLP媒体生成 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text frontend, Speaker Verification System, End-to-End Speech Translation … · 2026/9/25 2:50:00

Databasus 多语言 README 同步机制全解:assets/readme 目录结构与 Agent 工程规范
Databasus 多语言 README 同步机制全解:assets/readme 目录结构与 Agent 工程规范

数据库灾备 【免费下载链接】databasus PostgreSQL backup tool with Point-In-Time-Recovery and restore verification 项目地址: https://gitcode.com/gh_mirrors/po/databasus 点击查看 免费下载 Databasus 是开源的 PostgreSQL 备份工具(同时支持 … · 2026/9/25 2:50:00

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* 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 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* 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 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* 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 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码