Sui 通过多种接口暴露数据:具有固定方法词汇表的 JSON-RPC、当前 TypeScript SDK 使用的新版 gRPC API,以及由索引器支持、以关系视图呈现对象、交易、事件和检查点的 GraphQL 模式。GraphQL 允许客户端在单个 POST 中指定确切的响应结构,因此一笔交易、其效果以及它触及的对象可以在一次往返中获取,而无需多次 JSON-RPC 调用。请求以单个 POST 发送,包含 query、variables 和 operationName,内省查询会返回模式,使工具能够自动补全和验证字段。由于 GraphQL 端点通常由索引器支持,它可能落后于链尖,且模式字段和可用性因提供商和网络版本而异,因此在依赖某个字段之前进行内省至关重要。GraphQL 是只读的:交易仍通过 JSON-RPC 或 gRPC 提交。
Sui 的数据访问接口及 GraphQL 的定位
Sui 通过多个接口暴露链上数据,每个接口针对不同的消费方式进行了优化。JSON-RPC 是面向方法的接口:客户端调用诸如 sui_getObject 或 sui_getTransactionBlock 等命名方法,每次调用返回由该方法定义的固定负载。较新的 gRPC API 被当前 TypeScript SDK 使用,为许多相同操作提供类型化、适合流式传输的接口。由索引器支持的 GraphQL 模式与这些接口并存,作为对象、交易、事件和检查点的关系视图,分页和嵌套选择内置于模式本身。
这种区别很重要,因为这些接口不可互换。JSON-RPC 和 gRPC 是过程式的:你请求特定内容并收到特定结构。GraphQL 是声明式的:你描述想要的结构,服务器返回的正是该结构,这在仪表板或索引器需要同时获取多个相关实体时非常有用。Sui API 参考将这些接口记录为不同的数据访问选项,而 Sui RPC 指南详细介绍了 JSON-RPC 方法词汇表。
对于来自 JSON-RPC 世界的读者,思维模式的转变在于 GraphQL 不是交易提交的替代品。它是一个读取接口。你仍然通过 JSON-RPC 或 gRPC 广播交易;GraphQL 是你组装已发生事件读取侧视图的地方。
- JSON-RPC:固定的方法词汇表、固定的响应负载,每次调用处理一个关注点。
- gRPC:当前 TypeScript SDK 使用的类型化接口,适合流式传输和 SDK 集成。
- GraphQL:由索引器支持的关系模式,覆盖对象、交易、事件和检查点,支持嵌套选择和游标分页。
GraphQL 是什么,以及为什么客户端控制响应结构
GraphQL 是一种用于 API 的类型化查询语言和运行时,通过单个端点提供服务。它不暴露许多端点或方法,而是暴露类型和字段的模式,客户端发送描述它想要哪些字段的查询。服务器根据模式验证查询,并返回与查询结构镜像的 JSON 响应。GraphQL 学习文档将此描述为核心契约:一个端点、一个类型化模式,以及客户端指定的选择。
在 Sui JSON-RPC 工作流中,获取一笔交易、其效果以及它触及的对象通常意味着先调用一次获取交易,然后为每个需要的对象或效果进行额外调用。每次调用返回固定负载,因此你经常收到比实际使用更多的字段,并且仍需要更多调用来组装完整视图。GraphQL 颠覆了这一点:你编写一个查询,将交易、其效果以及它触及的对象作为嵌套字段选择,服务器返回一个仅包含这些字段的单一响应。
这就是减少往返次数的机制。并不是 GraphQL 在每字节上天生更快;而是客户端可以将多实体读取表达为一个操作,而不是 N 个操作。对于反复组装相同关系视图的索引器和仪表板,这种差异会累积。
为什么 GraphQL 对索引器和仪表板很重要
索引器和仪表板有一个共同模式:它们需要一笔交易加上其效果加上它触及的对象,通常需要按顺序处理许多交易。在 JSON-RPC 中,这种模式会变成调用的扇出,每次调用消耗请求单元并增加一次往返。GraphQL 将扇出折叠为带有嵌套选择的单个查询,从而减少相同逻辑读取的往返次数和请求单元消耗。
基于游标的分页是模式的一部分,而不是事后添加的。你无需手动跟踪偏移量,而是请求一页并收到一个游标,将其传入下一个查询。这与 Sui 对象读取和动态字段分页指南中针对 JSON-RPC 描述的模式相同,但以模式字段的形式表达。对于分页浏览检查点或事件的仪表板,游标成为稳定的延续令牌。
关系视图还有助于处理在面向方法的 API 中很别扭的连接。如果你需要一个事件及其发出交易,或者一个检查点及其包含的交易,模式可以直接表达这种关系。Sui 检查点流和账本服务指南涵盖了该图景的检查点部分,而 Sui 交易效果和对象变更指南涵盖了当你确实需要解析效果时效果的结构。
- 一个查询可以选择一笔交易、其效果以及它触及的对象。
- 游标分页是模式字段,而不是手动偏移量计算。
- 嵌套选择减少了组装关系视图所需的调用次数。
- 更少的调用通常意味着相同读取消耗更少的请求单元。
GraphQL 请求与 JSON-RPC 请求有何不同
GraphQL 请求是向 GraphQL 端点发送的单个 HTTP POST,其 JSON 正文包含 query、variables 以及可选的 operationName。query 字段保存 GraphQL 文档,variables 保存该文档引用的值,operationName 在文档包含多个操作时用于区分。响应是一个 JSON 对象,包含 data 字段,出错时还包含 errors 数组。
JSON-RPC 请求也是 POST,但其正文是 JSON-RPC 2.0 信封,包含 jsonrpc、method、params 和 id。JSON-RPC 2.0 规范定义了这种面向方法的模型:客户端命名一个方法并传递位置或命名参数,服务器返回结果或按相同 id 键控的错误。请求本身没有模式协商;方法词汇表由服务器固定。
实际后果是 GraphQL 请求具有 JSON-RPC 请求所没有的自描述性。GraphQL 查询命名它想要的字段,因此响应结构在请求中可见。JSON-RPC 调用命名一个方法,响应结构在别处定义。这就是为什么 GraphQL 工具可以根据模式自动补全和验证,而 JSON-RPC 工具依赖文档或生成的客户端。
// JSON-RPC 2.0 request envelope (method-oriented)
{
"jsonrpc": "2.0",
"id": 1,
"method": "sui_getObject",
"params": ["0xOBJECT_ID", { "showType": true }]
}
// GraphQL request body (client-specified selection)
{
"query": "query GetObject($id: SuiAddress!) { object(address: $id) { address version digest } }",
"variables": { "id": "0xOBJECT_ID" },
"operationName": "GetObject"
}内省:在依赖字段之前确认模式
内省是 GraphQL 机制,将模式本身作为数据返回。客户端可以询问存在哪些类型、每个类型有哪些字段,以及这些字段接受哪些参数。GraphQL 学习文档将内省描述为自动补全、验证和模式浏览器等工具的基础。对于 Sui,内省是确认你打算使用的字段确实存在于所查询端点上的可靠方式。
这很重要,因为 Sui 的 GraphQL 模式和可用性因提供商和网络版本而异。一个端点上存在的字段可能在另一个端点上缺失或重命名,针对测试网端点有效的查询可能在主网上失败。先进行内省将运行时失败转化为已知约束。它还告诉你内省是否启用:在生产环境中禁用内省很常见,当它被禁用时,你必须依赖固定查询而不是生成的工具。
下面的示例发送一个内省查询以确认模式可达。它故意很小:它询问查询类型的名称及其几个字段,这足以证明端点以模式响应。完整的内省查询返回整个类型系统,规模要大得多。
// introspection.mjs — confirm the GraphQL schema is reachable
const ENDPOINT = process.env.SUI_GRAPHQL_ENDPOINT;
const introspectionQuery = `
query IntrospectQueryType {
__schema {
queryType {
name
fields {
name
description
}
}
}
}
`;
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ query: introspectionQuery, operationName: "IntrospectQueryType" })
});
const json = await res.json();
if (json.errors) {
console.error("Introspection failed:", json.errors);
process.exit(1);
}
const queryType = json.data.__schema.queryType;
console.log("Query type:", queryType.name);
console.log("Fields:", queryType.fields.map((f) => f.name).join(", "));一个可运行的 Node.js 查询:获取交易及其效果
确认模式后,可以使用变量编写具体查询来选择一笔交易及其效果。下面的示例使用 fetch(当前 Node.js 版本中可用),并将交易摘要作为变量传递,而不是将其插入查询字符串。使用变量是推荐模式,因为它保持查询文档稳定,并让服务器根据模式类型验证值。
该查询按摘要选择一笔交易,并请求其效果以及它触及的对象。确切的字段名取决于端点暴露的模式版本,这就是为什么内省步骤要先进行。如果字段名不同,服务器会返回一个命名未知字段的错误,你应根据内省的模式调整查询,而不是猜测。
响应是一个 JSON 对象,其 data 结构与查询镜像。由于客户端指定了选择,之后无需过滤未使用的字段。这就是实践中的往返减少:一次 POST 同时返回交易、其效果和相关对象。
// query-transaction.mjs — fetch a transaction and its effects in one round-trip
const ENDPOINT = process.env.SUI_GRAPHQL_ENDPOINT;
const DIGEST = process.env.SUI_TX_DIGEST;
const query = `
query TransactionWithEffects($digest: String!) {
transaction(digest: $digest) {
digest
effects {
status
timestamp
objectChanges {
address
inputState
outputState
}
}
}
}
`;
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
query,
variables: { digest: DIGEST },
operationName: "TransactionWithEffects"
})
});
const json = await res.json();
if (json.errors) {
console.error("Query errors:", json.errors);
process.exit(1);
}
console.log(JSON.stringify(json.data.transaction, null, 2));针对你自己的端点测量往返次数和字节数
GraphQL 对给定工作负载的价值是一个经验问题,诚实的回答方式是针对你实际使用的端点进行测量。下表是一个模板:用你自己的观察结果填写,而不是依赖其他环境的数字。将相同的逻辑任务运行两次,一次作为一系列 JSON-RPC 调用,一次作为单个 GraphQL 查询,并记录每次的调用次数、字节数和往返次数。
记录每页使用的分页游标,以便比较可复现。如果任务跨多页,记下游标值,以便其他读者可以重放相同的序列。字节数可以从响应正文长度测量,往返次数可以计为发出的 HTTP 请求数。保持两个列中的任务定义相同,以便比较有意义。
由于提供商行为各异,将任何单次测量视为特定于该端点、网络和时间。方法才是可迁移的:定义任务,运行两个接口,并记录数字。
- 任务:用一句话描述逻辑读取,例如“获取交易 X 及其效果和触及的对象”。
- JSON-RPC 调用:计算发出的方法调用次数。
- GraphQL 调用:计算发出的 POST 请求次数。
- 总字节数:汇总每个接口的响应正文大小。
- 往返次数:计算 HTTP 请求数,包括重试。
- 使用的分页游标:记录每页的游标值,以便运行可重放。
GraphQL 接口的局限性和权衡
GraphQL 端点通常由索引器支持,这意味着它可能落后于链尖。刚刚最终确定的交易可能尚未出现在索引中,因此提交后立即读取的仪表板可能会观察到间隙。这是索引管道的属性,不是 GraphQL 的缺陷,但它改变了你设计写后读流程的方式。如果你需要尽可能最新的视图,JSON-RPC 或 gRPC 可能是该特定读取的更好接口。
模式字段和可用性因提供商和网络版本而异。一个端点上存在的字段可能在另一个端点上缺失,在测试网上有效的查询可能需要在主网上调整。在依赖字段之前进行内省是实际的缓解措施,但并不能消除差异。当生产环境中禁用内省时(这很常见),生成的工具无法发现模式,必须手动维护固定查询。
GraphQL 是只读的。它不提交交易;这仍然是 JSON-RPC 或 gRPC 的工作。因此,一个完整的应用程序使用多个接口:GraphQL 用于关系读取,JSON-RPC 或 gRPC 用于提交和不能落后的读取。Sui RPC WebSocket 订阅指南涵盖了流式传输方面,适合需要推送式更新而非轮询的读者。
- 由索引器支持的端点可能落后于链尖。
- 模式字段和可用性因提供商和网络版本而异。
- 生产环境中通常禁用内省,需要固定查询。
- GraphQL 是只读的;交易提交仍使用 JSON-RPC 或 gRPC。
- 完整的应用程序通常使用多个数据访问接口。
排查常见的 GraphQL 查询失败
大多数 GraphQL 失败属于少数几类,错误响应通常会指出原因。验证错误意味着查询引用了模式中不存在的字段或参数;修复方法是内省并更正字段名或参数类型。执行错误意味着查询有效但解析器失败,通常是因为请求的实体不存在或索引器尚未看到它。
缺少 data 字段并带有 errors 数组是标准的 GraphQL 错误结构。先阅读 errors 条目:它们包含消息,通常还有指向失败字段的路径。如果错误提到未知字段,则该端点上的模式与你假设的不同。如果错误提到类型不匹配,请检查你的变量是否与声明的参数类型匹配。
如果内省本身失败,端点可能禁用了内省,或者端点根本不是 GraphQL 端点。确认 URL 和 HTTP 方法:GraphQL 是向单个端点发送 POST,而不是方法调用。如果响应是 HTML 而不是 JSON,请求很可能命中了 Web 服务器而不是 GraphQL 处理器。
- 验证错误:字段或参数不在模式中——内省并更正。
- 执行错误:解析器失败——检查实体存在性和索引器新鲜度。
- 错误中出现未知字段:模式与你的假设不同。
- 类型不匹配:变量与声明的参数类型不匹配。
- 内省失败:内省被禁用或端点错误。
- HTML 响应:请求命中了 Web 服务器,而不是 GraphQL 处理器。
在 Sui GraphQL 接口上构建的后续步骤
实际路径是先内省,然后针对确认的模式编写查询,再针对你自己的端点测量工作负载。从选择单个实体的小查询开始,确认响应结构,然后在基础工作正常后扩展到嵌套选择。保持查询文档稳定,并将值作为变量传递,以便服务器根据模式验证它们。
对于运行索引器或仪表板的团队,OnFinality Learn 中心收集了有关 Sui 数据访问的相关指南,Sui 网络页面涵盖了网络背景。如果你正在为生产工作负载评估端点,RPC 定价页面和 API 服务页面描述了商业方面,而 Sui RPC 指南仍然是提交时仍需要的 JSON-RPC 方法词汇表的参考。
一个合理的下一个实验是,取一个当前扇出为多个 JSON-RPC 调用的仪表板面板,将其重写为单个 GraphQL 查询,然后填写测量部分的结果表。这为你自己的环境提供了具体、可复现的比较,而不是借用来的数字。
- 在编写查询之前先内省端点。
- 从单实体查询开始,然后扩展到嵌套选择。
- 将值作为变量传递,以便服务器验证它们。
- 针对两个接口测量一个真实的仪表板面板。
- 保留 JSON-RPC 或 gRPC 用于交易提交和最新读取。