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

            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