Docs
搜索⌘ K
  • 主页
  • 关于 The Graph
  • 支持的网络
  • 协议合约
  • 子图
    • 子流
      • 代币 API
        • AI Suite
          • 索引
            • 资源
              子图 > 查询

              13 分钟

              查询最佳实践

              Graph提供了一种去中心化的方式来查询区块链中的数据。它的数据是通过GraphQL API公开的,这使得使用GraphQL语言进行查询更加容易。

              学习基本的 GraphQL 语言规则和最佳做法,以优化您的子图。


              查询GraphQL API

              GraphQL查询的剖析

              与REST API不同,GraphQL API构建在定义可以执行哪些查询的模式之上。

              例如,使用token查询获取代币如下所示:

              1query GetToken($id: ID!) {2  token(id: $id) {3    id4    owner5  }6}

              它将返回以下可预测的 JSON 响应(当传递适当的 $id 变量值时):

              1{2  "token": {3    "id": "...",4    "owner": "..."5  }6}

              GraphQL 查询使用基于规范⁠定义的 GraphQL 语言。

              上述GetToken查询由多语言部分组成(用[...]占位符替换):

              1query [operationName]([variableName]: [variableType]) {2  [queryName]([argumentName]: [variableName]) {3    # "{ ... }" 代表一个选择集, 我们正在从`queryName`查询域。4    [field]5    [field]6  }7}

              写入 GraphQL 查询规则

              • 每个操作只能使用一次queryName。
              • 每个字段必须只能在选择中使用一次 (我们不能在 token 下查询id 两次)。
              • 有些字段或查询(如tokens)返回了需要选择子字段的复杂类型。 预期时不提供选择(或在不预期时提供选择――例如在id上――会引起错误。 要知道一个字段类型,请参阅Graph Explorer。
              • 分配给参数的任何变量都必须匹配其类型。
              • 在给定的变量列表中,每个变量必须是唯一的。
              • 必须使用所有已定义的变量。

              注意:如果不遵循这些规则,将导致从 The Graph API中发生错误。

              有关包含代码示例的完整规则列表,请查看GraphQL Validations guide。

              向 GraphQL API 发送查询

              GraphQL 是一种通过 HTTP 传输的语言和一组协议。

              这意味着您可以使用标准的 fetch (nomatily or through @whatwg-node/fetch 或 isomorphic-fetch)查询一个 GraphQL API。

              然而,正如[“查询申请”](/subgraphs/querying/from-an-application/)中提到的那样,建议使用 graph-client ,支持以下独特功能:

              • 跨链子图处理: 在一个查询中从多个子图进行查询
              • 自动区块跟踪⁠
              • 自动分页⁠
              • 完全类型化的结果

              以下是如何使用 Graph-client 查询The Graph:

              1import { execute } from '../.graphclient'23const query = `4query GetToken($id: ID!) {5  token(id: $id) {6    id7    owner8  }9}10`11const variables = { id: '1' }1213async function main() {14  const result = await execute(query, variables)15  // `result` is fully typed!16  console.log(result)17}1819main()

              更多GraphQL客户端替代方案在“从应用程序查询”中介绍。


              最佳实践

              始终编写静态查询

              一个常见的(不好的) 实践是动态构建查询字符串,如下所示:

              1const id = params.id2const fields = ['id', 'owner']3const query = `4query GetToken {5  token(id: ${id}) {6    ${fields.join('\n')}7  }8}9`1011// Execute query...

              虽然上面的代码片段产生了一个有效的 GraphQL 查询,但它有许多缺点 :

              • 它使得更难理解 整个查询
              • 开发者负责安全清理字符串内插值
              • 不将变量的值作为请求参数的一部分发送以防止服务器侧可能缓存
              • 它阻止工具静态分析查询 (例如: Linter, 或类型代工具)

              因此,建议始终将查询写为静态字符串:

              1import { execute } from 'your-favorite-graphql-client'23const id = params.id4const query = `5query GetToken($id: ID!) {6  token(id: $id) {7    id8    owner9  }10}11`1213const result = await execute(query, {14  variables: {15    id,16  },17})

              这样做会带有许多优点:

              • 易读和维护 查询
              • GraphQL 服务器处理变量净化
              • **变量可以缓存到 **服务器级别
              • 查询可以通过工具进行静态分析 (在以下章节中更多关于此问题)

              如何在静态查询中有条件地包含字段

              您可能只想在特定条件下包含 ‘所有者’ 字段。

              为此,你可以将@include(if:...)的指令应用如下:

              1import { execute } from 'your-favorite-graphql-client'23const id = params.id4const query = `5query GetToken($id: ID!, $includeOwner: Boolean) {6  token(id: $id) {7    id8    owner @include(if: $includeOwner)9  }10}11`1213const result = await execute(query, {14  variables: {15    id,16    includeOwner: true,17  },18})

              注意: 相反的指令是 @skip(if: ...) 。

              问你所想

              GraphQL以其“问你所想”的口号而闻名。

              因此,在GraphQL中,不单独列出所有可用字段,就无法获取所有可用字段。

              • 在查询GraphQL API时,请始终考虑只查询实际使用的字段。
              • 请确保查询仅获取您实际需要的更多实体。 默认情况下,查询将会在集合中获取100个实体,这通常比实际使用的要多得多,如用于显示用户。这不仅适用于查询中的顶层集合,更适用于实体嵌套集合。

              例如,在以下查询中:

              1query listTokens {2  tokens {3    # 获取最多100个token4    id5    transactions {6      # 获取最多100个交易7      id8    }9  }10}

              该响应可以包含每100个代币交易中的100个交易。

              如果应用程序只需要10笔交易,查询应在交易字段中明确设置 first: 10。

              使用单个查询请求多个记录

              默认情况下,子图表有一个单数实体用于一个记录。对于多个记录,请使用复数实体和过滤器:其中:{id_in:[X,Y,Z]}或者其中: {volume_gt:100000}。

              低效查询示例:

              1query SingleRecord {2  entity(id: X) {3    id4    name5  }6}7query SingleRecord {8  entity(id: Y) {9    id10    name11  }12}

              优化查询示例:

              1query ManyRecords {2  entities(where: { id_in: [X, Y] }) {3    id4    name5  }6}

              在单个请求中合并多个查询

              您的应用程序可能需要查询多种类型的数据,如下所示:

              1import { execute } from "your-favorite-graphql-client"23const tokensQuery = `4query GetTokens {5  tokens(first: 50) {6    id7    owner8  }9}10`11const countersQuery = `12query GetCounters {13  counters {14    id15    value16  }17}18`1920const [tokens, counters] = Promise.all(21  [22    tokensQuery,23    countersQuery,24  ].map(execute)25)

              虽然这个实现是完全有效的,但它需要使用GraphQL API进行两次交互。

              幸运的是,在同一GraphQL请求中发送多个查询也是有效的,如下所示:

              1import { execute } from "your-favorite-graphql-client"23const query = `4query GetTokensandCounters {5  tokens(first: 50) {6    id7    owner8  }9  counters {10    id11    value12  }13}14`1516const  { result: { tokens, counters } } = execute(query)

              这个方法将改进整体性能,减少网络上花费的时间(将你节省一次回程旅行到 API),并提供一个更简洁的实现。

              利用GraphQL片段

              编写GraphQL查询的一个有用功能是GraphQL片段。

              查看以下查询,您会注意到某些字段在多个选择集({ ... }) 中重复:

              1query {2  bondEvents {3    id4    newDelegate {5      id6      active7      status8    }9    oldDelegate {10      id11      active12      status13    }14  }15}

              此类重复字段(id, active, status)会带来许多问题:

              • 更广泛的查询变得更难阅读。
              • 当使用基于查询生成类型的 TypeScript 类型的工具 (more on that in the last section), newDelegate和oldDelegate将导致两个不同的内联接口。

              查询的重构版本如下:

              1query {2  bondEvents {3    id4    newDelegate {5      ...DelegateItem6    }7    oldDelegate {8      ...DelegateItem9    }10  }11}1213# 我们在代码转换器定义了一个碎片(子类型)14# 将查询中重复的域分解15fragment DelegateItem on Transcoder {16  id17  active18  status19}

              使用 GraphQL fragment 将提高可读性(尤其在规模上),并导致更好的 TypeScript 类型生成。

              当使用类型生成工具时,上面的查询将生成一个适当的DelegateItemFragment 类型(_see last “Tools” section _)。

              GraphQL片段的注意事项

              片段必须是一种类型

              片段不能基于不适用的类型,简而言之,基于没有字段的类型:

              1fragment MyFragment on BigInt {2  # ...3}

              BigInt 是一个 scalar (原生”plain” 类型),不能用作碎片的基础。

              如何传播片段

              片段是在特定类型上定义的,应该在查询中相应地使用。

              例子:

              1query {2  bondEvents {3    id4    newDelegate {5      ...VoteItem # Error! `VoteItem` cannot be spread on `Transcoder` type6    }7    oldDelegate {8      ...VoteItem9    }10  }11}1213fragment VoteItem on Vote {14  id15  voter16}

              newDelegate和oldDelegate都是Transcoder的类型。

              不可能在这里散布Vote型号的片段。

              将片段定义为数据的原子业务单元。

              必须根据它们的用途来定义GraphQL Fragment 。

              对于大多数用例,为每个类型定义一个片段(在重复使用字段或生成类型的情况下)就足够了。

              以下是使用Fragment的经验法则:

              • 当同类字段重复查询时,将它们分组为片段。
              • 当重复类似但不相同的字段时,创建多个片段,例如:
              1# base fragment (主要在上架中使用)2fragment Voter on Vote {3  id4  voter5}67# extended fragment (当查询投票中一个选项的细节)8fragment VoteWithPoll on Vote {9  id10  voter11  choiceID12  poll {13    id14    proposal15  }16}

              重要工具

              GraphQL基于web的浏览器

              通过在您的应用程序中运行这些查询来绕过它们,可能是繁琐的。 为此,请不要犹豫地使用 Graph Explorer 来测试您的查询,然后将它们添加到您的应用程序中。 Graph Explorer将为您提供一个预配置的 GraphQL 播放场,以测试您的查询。

              如果您正在寻找一种更灵活的方式来调试或测试您的查询, 其他类似的网络工具也可用,如 Altair⁠ 和 GraphiQL⁠。

              GraphQL Linting

              为了跟上提到的最佳实践和语法规则,强烈建议使用以下工作流和IDE工具。

              GraphQL ESLint

              GraphQL ESLint⁠将帮助您保持在 GraphQL 最佳做法之外的零努力状态。

              设置“推荐操作”⁠配置将强制执行基本规则,例如:

              • @graphql-eslint/fields-on-correct-type: 字段是否用于适当类型?
              • @graphql-eslint/no-used 变量: 某个变量是否不使用?
              • 还有更多!

              这将允许您在运作场上捕获错误——甚至不需要测试查询 或者在生产中运行它们!

              IDE插件

              VSCode和GraphQL

              [GraphQL VSCode扩展](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql)是开发工作流程中一个很好的补充,可以获得:⁠

              • 语法高亮
              • 自动完成建议
              • 针对模式的验证
              • 代码片段
              • 转到片段和输入类型的定义

              如果您使用 graphql-eslint, ESLint VSCode 扩展⁠ 是一个必须直观的错误和警告包含在您的代码中是正确的。

              WebStorm/Intellij和GraphQL

              JS GraphQL插件⁠ 在使用 GraphQL 时将通过提供以下方式大大改进您的体验:

              • 语法高亮
              • 自动完成建议
              • 针对模式的验证
              • 代码片段

              了解更多关于此主题的信息,请查阅WebStorm 文章⁠,该插件的所有主要功能。

              ⁠在GitHub上编辑⁠

              管理API 密钥从应用程序中进行查询
              在此页面上
              • 查询GraphQL API
              • GraphQL查询的剖析
              • 写入 GraphQL 查询规则
              • 向 GraphQL API 发送查询
              • 最佳实践
              • 始终编写静态查询
              • 如何在静态查询中有条件地包含字段
              • 问你所想
              • 使用单个查询请求多个记录
              • 在单个请求中合并多个查询
              • 利用GraphQL片段
              • GraphQL片段的注意事项
              • 片段必须是一种类型
              • 重要工具
              • GraphQL基于web的浏览器
              • GraphQL Linting
              • IDE插件
              The GraphStatusTestnetBrand AssetsForum安全Privacy PolicyTerms of Service