Docs
Search⌘ K
  • Home
  • About The Graph
  • Supported Networks
  • Protocol Contracts
  • Subgraphs
    • Substreams
      • Token API
        • Indexing
          • Resources
            Subgraphs > Querying

            9 minutes

            Querying Best Practices

            The Graph provides a decentralized way to query data from blockchains. Its data is exposed through a GraphQL API, making it easier to query with the GraphQL language.

            Learn the essential GraphQL language rules and best practices to optimize your Subgraph.


            Querying a GraphQL API

            The Anatomy of a GraphQL Query

            Unlike REST API, a GraphQL API is built upon a Schema that defines which queries can be performed.

            For example, a query to get a token using the token query will look as follows:

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

            which will return the following predictable JSON response (when passing the proper $id variable value):

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

            GraphQL queries use the GraphQL language, which is defined upon a specification⁠.

            The above GetToken query is composed of multiple language parts (replaced below with [...] placeholders):

            1query [operationName]([variableName]: [variableType]) {2  [queryName]([argumentName]: [variableName]) {3    # "{ ... }" express a Selection-Set, we are querying fields from `queryName`.4    [field]5    [field]6  }7}

            Rules for Writing GraphQL Queries

            • Each queryName must only be used once per operation.
            • Each field must be used only once in a selection (we cannot query id twice under token)
            • Some fields or queries (like tokens) return complex types that require a selection of sub-field. Not providing a selection when expected (or providing one when not expected - for example, on id) will raise an error. To know a field type, please refer to Graph Explorer.
            • Any variable assigned to an argument must match its type.
            • In a given list of variables, each of them must be unique.
            • All defined variables must be used.

            Note: Failing to follow these rules will result in an error from The Graph API.

            For a complete list of rules with code examples, check out GraphQL Validations guide.

            Sending a query to a GraphQL API

            GraphQL is a language and set of conventions that transport over HTTP.

            It means that you can query a GraphQL API using standard fetch (natively or via @whatwg-node/fetch or isomorphic-fetch).

            However, as mentioned in “Querying from an Application”, it’s recommended to use graph-client, which supports the following unique features:

            • Cross-chain Subgraph Handling: Querying from multiple Subgraphs in a single query
            • Automatic Block Tracking⁠
            • Automatic Pagination⁠
            • Fully typed result

            Here’s how to query The Graph with graph-client:

            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()

            More GraphQL client alternatives are covered in “Querying from an Application”.


            Best Practices

            Always write static queries

            A common (bad) practice is to dynamically build query strings as follows:

            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...

            While the above snippet produces a valid GraphQL query, it has many drawbacks:

            • it makes it harder to understand the query as a whole
            • developers are responsible for safely sanitizing the string interpolation
            • not sending the values of the variables as part of the request parameters prevent possible caching on server-side
            • it prevents tools from statically analyzing the query (ex: Linter, or type generations tools)

            For this reason, it is recommended to always write queries as static strings:

            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})

            Doing so brings many advantages:

            • Easy to read and maintain queries
            • The GraphQL server handles variables sanitization
            • Variables can be cached at server-level
            • Queries can be statically analyzed by tools (more on this in the following sections)

            How to include fields conditionally in static queries

            You might want to include the owner field only on a particular condition.

            For this, you can leverage the @include(if:...) directive as follows:

            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})

            Note: The opposite directive is @skip(if: ...).

            Ask for what you want

            GraphQL became famous for its “Ask for what you want” tagline.

            For this reason, there is no way, in GraphQL, to get all available fields without having to list them individually.

            • When querying GraphQL APIs, always think of querying only the fields that will be actually used.
            • Make sure queries only fetch as many entities as you actually need. By default, queries will fetch 100 entities in a collection, which is usually much more than what will actually be used, e.g., for display to the user. This applies not just to top-level collections in a query, but even more so to nested collections of entities.

            For example, in the following query:

            1query listTokens {2  tokens {3    # will fetch up to 100 tokens4    id5    transactions {6      # will fetch up to 100 transactions7      id8    }9  }10}

            The response could contain 100 transactions for each of the 100 tokens.

            If the application only needs 10 transactions, the query should explicitly set first: 10 on the transactions field.

            Use a single query to request multiple records

            By default, Subgraphs have a singular entity for one record. For multiple records, use the plural entities and filter: where: {id_in:[X,Y,Z]} or where: {volume_gt:100000}

            Example of inefficient querying:

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

            Example of optimized querying:

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

            Combine multiple queries in a single request

            Your application might require querying multiple types of data as follows:

            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)

            While this implementation is totally valid, it will require two round trips with the GraphQL API.

            Fortunately, it is also valid to send multiple queries in the same GraphQL request as follows:

            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)

            This approach will improve the overall performance by reducing the time spent on the network (saves you a round trip to the API) and will provide a more concise implementation.

            Leverage GraphQL Fragments

            A helpful feature to write GraphQL queries is GraphQL Fragment.

            Looking at the following query, you will notice that some fields are repeated across multiple Selection-Sets ({ ... }):

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

            Such repeated fields (id, active, status) bring many issues:

            • More extensive queries become harder to read.
            • When using tools that generate TypeScript types based on queries (more on that in the last section), newDelegate and oldDelegate will result in two distinct inline interfaces.

            A refactored version of the query would be the following:

            1query {2  bondEvents {3    id4    newDelegate {5      ...DelegateItem6    }7    oldDelegate {8      ...DelegateItem9    }10  }11}1213# we define a fragment (subtype) on Transcoder14# to factorize repeated fields in the query15fragment DelegateItem on Transcoder {16  id17  active18  status19}

            Using GraphQL fragment will improve readability (especially at scale) and result in better TypeScript types generation.

            When using the types generation tool, the above query will generate a proper DelegateItemFragment type (see last “Tools” section).

            GraphQL Fragment do’s and don’ts

            Fragment base must be a type

            A Fragment cannot be based on a non-applicable type, in short, on type not having fields:

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

            BigInt is a scalar (native “plain” type) that cannot be used as a fragment’s base.

            How to spread a Fragment

            Fragments are defined on specific types and should be used accordingly in queries.

            Example:

            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 and oldDelegate are of type Transcoder.

            It is not possible to spread a fragment of type Vote here.

            Define Fragment as an atomic business unit of data

            GraphQL Fragments must be defined based on their usage.

            For most use-case, defining one fragment per type (in the case of repeated fields usage or type generation) is sufficient.

            Here is a rule of thumb for using fragments:

            • When fields of the same type are repeated in a query, group them in a Fragment.
            • When similar but different fields are repeated, create multiple fragments, for instance:
            1# base fragment (mostly used in listing)2fragment Voter on Vote {3  id4  voter5}67# extended fragment (when querying a detailed view of a vote)8fragment VoteWithPoll on Vote {9  id10  voter11  choiceID12  poll {13    id14    proposal15  }16}

            The Essential Tools

            GraphQL web-based explorers

            Iterating over queries by running them in your application can be cumbersome. For this reason, don’t hesitate to use Graph Explorer to test your queries before adding them to your application. Graph Explorer will provide you a preconfigured GraphQL playground to test your queries.

            If you are looking for a more flexible way to debug/test your queries, other similar web-based tools are available such as Altair⁠ and GraphiQL⁠.

            GraphQL Linting

            In order to keep up with the mentioned above best practices and syntactic rules, it is highly recommended to use the following workflow and IDE tools.

            GraphQL ESLint

            GraphQL ESLint⁠ will help you stay on top of GraphQL best practices with zero effort.

            Setup the “operations-recommended”⁠ config will enforce essential rules such as:

            • @graphql-eslint/fields-on-correct-type: is a field used on a proper type?
            • @graphql-eslint/no-unused variables: should a given variable stay unused?
            • and more!

            This will allow you to catch errors without even testing queries on the playground or running them in production!

            IDE plugins

            VSCode and GraphQL

            The GraphQL VSCode extension⁠ is an excellent addition to your development workflow to get:

            • Syntax highlighting
            • Autocomplete suggestions
            • Validation against schema
            • Snippets
            • Go to definition for fragments and input types

            If you are using graphql-eslint, the ESLint VSCode extension⁠ is a must-have to visualize errors and warnings inlined in your code correctly.

            WebStorm/Intellij and GraphQL

            The JS GraphQL plugin⁠ will significantly improve your experience while working with GraphQL by providing:

            • Syntax highlighting
            • Autocomplete suggestions
            • Validation against schema
            • Snippets

            For more information on this topic, check out the WebStorm article⁠ which showcases all the plugin’s main features.

            ⁠Edit on GitHub⁠

            Managing API keysQuerying from an App
            On this page
            • Querying a GraphQL API
            • The Anatomy of a GraphQL Query
            • Rules for Writing GraphQL Queries
            • Sending a query to a GraphQL API
            • Best Practices
            • Always write static queries
            • How to include fields conditionally in static queries
            • Ask for what you want
            • Use a single query to request multiple records
            • Combine multiple queries in a single request
            • Leverage GraphQL Fragments
            • GraphQL Fragment do’s and don’ts
            • Fragment base must be a type
            • The Essential Tools
            • GraphQL web-based explorers
            • GraphQL Linting
            • IDE plugins
            The GraphStatusTestnetBrand AssetsForumSecurityPrivacy PolicyTerms of Service