GraphQL API
Reading time: 10 min
This guide explains the GraphQL Query API that is used for The Graph Protocol.
In your subgraph schema you define types called Entities
. For each Entity
type, an entity
and entities
field will be generated on the top-level Query
type. Note that query
does not need to be included at the top of the graphql
query when using The Graph.
Query for a single Token
entity defined in your schema:
{token(id: "1") {idowner}}
Note: When querying for a single entity, the id
field is required, and it must be a string.
Query all Token
entities:
{tokens {idowner}}
When querying a collection, the orderBy
parameter may be used to sort by a specific attribute. Additionally, the orderDirection
can be used to specify the sort direction, asc
for ascending or desc
for descending.
{tokens(orderBy: price, orderDirection: asc) {idowner}}
As of Graph Node entities can be sorted on the basis of nested entities.
In the following example, we sort the tokens by the name of their owner:
{tokens(orderBy: owner__name, orderDirection: asc) {idowner {name}}}
Currently, you can sort by one-level deep String
or ID
types on @entity
and @derivedFrom
fields. Unfortunately, , sorting by fields which are arrays and nested entities is not yet supported.
When querying a collection, the first
parameter can be used to paginate from the beginning of the collection. It is worth noting that the default sort order is by ID in ascending alphanumeric order, not by creation time.
Further, the skip
parameter can be used to skip entities and paginate. e.g. first:100
shows the first 100 entities and first:100, skip:100
shows the next 100 entities.
Queries should avoid using very large skip
values since they generally perform poorly. For retrieving a large number of items, it is much better to page through entities based on an attribute as shown in the last example.
Query the first 10 tokens:
{tokens(first: 10) {idowner}}
To query for groups of entities in the middle of a collection, the skip
parameter may be used in conjunction with the first
parameter to skip a specified number of entities starting at the beginning of the collection.
Query 10 Token
entities, offset by 10 places from the beginning of the collection:
{tokens(first: 10, skip: 10) {idowner}}
If a client needs to retrieve a large number of entities, it is much more performant to base queries on an attribute and filter by that attribute. For example, a client would retrieve a large number of tokens using this query:
query manyTokens($lastID: String) {tokens(first: 1000, where: { id_gt: $lastID }) {idowner}}
The first time, it would send the query with lastID = ""
, and for subsequent requests would set lastID
to the id
attribute of the last entity in the previous request. This approach will perform significantly better than using increasing skip
values.
You can use the where
parameter in your queries to filter for different properties. You can filter on multiple values within the where
parameter.
Query challenges with failed
outcome:
{challenges(where: { outcome: "failed" }) {challengeroutcomeapplication {id}}}
You can use suffixes like _gt
, _lte
for value comparison:
{applications(where: { deposit_gt: "10000000000" }) {idwhitelisteddeposit}}
You can also filter entities by the _change_block(number_gte: Int)
- this filters entities which were updated in or after the specified block.
This can be useful if you are looking to fetch only entities which have changed, for example since the last time you polled. Or alternatively it can be useful to investigate or debug how entities are changing in your subgraph (if combined with a block filter, you can isolate only entities that changed in a specific block).
{applications(where: { _change_block: { number_gte: 100 } }) {idwhitelisteddeposit}}
Filtering on the basis of nested entities is possible in the fields with the _
suffix.
This can be useful if you are looking to fetch only entities whose child-level entities meet the provided conditions.
{challenges(where: { application_: { id: 1 } }) {challengeroutcomeapplication {id}}}
As of Graph Node you can group multiple parameters in the same where
argument using the and
or the or
operators to filter results based on more than one criteria.
In the following example, we are filtering for challenges with outcome
succeeded
and number
greater than or equal to 100
.
{challenges(where: { and: [{ number_gte: 100 }, { outcome: "succeeded" }] }) {challengeroutcomeapplication {id}}}
Syntactic sugar: You can simplify the above query by removing the and
operator by passing a sub-expression separated by commas.
{challenges(where: { number_gte: 100, outcome: "succeeded" }) {challengeroutcomeapplication {id}}}
In the following example, we are filtering for challenges with outcome
succeeded
or number
greater than or equal to 100
.
{challenges(where: { or: [{ number_gte: 100 }, { outcome: "succeeded" }] }) {challengeroutcomeapplication {id}}}
Note: When constructing queries, it is important to consider the performance impact of using the or
operator. While or
can be a useful tool for broadening search results, it can also have significant costs. One of the main issues with or
is that it can cause queries to slow down. This is because or
requires the database to scan through multiple indexes, which can be a time-consuming process. To avoid these issues, it is recommended that developers use and operators instead of or whenever possible. This allows for more precise filtering and can lead to faster, more accurate queries.
Full list of parameter suffixes:
__not_gt_lt_gte_lte_in_not_in_contains_contains_nocase_not_contains_not_contains_nocase_starts_with_starts_with_nocase_ends_with_ends_with_nocase_not_starts_with_not_starts_with_nocase_not_ends_with_not_ends_with_nocase
Please note that some suffixes are only supported for specific types. For example, Boolean
only supports _not
, _in
, and _not_in
, but _
is available only for object and interface types.
In addition, the following global filters are available as part of where
argument:
_change_block(number_gte: Int)
You can query the state of your entities not just for the latest block, which is the default, but also for an arbitrary block in the past. The block at which a query should happen can be specified either by its block number or its block hash by including a block
argument in the toplevel fields of queries.
The result of such a query will not change over time, i.e., querying at a certain past block will return the same result no matter when it is executed, with the exception that if you query at a block very close to the head of the chain, the result might change if that block turns out to not be on the main chain and the chain gets reorganized. Once a block can be considered final, the result of the query will not change.
Note that the current implementation is still subject to certain limitations that might violate these guarantees. The implementation can not always tell that a given block hash is not on the main chain at all, or that the result of a query by block hash for a block that can not be considered final yet might be influenced by a block reorganization running concurrently with the query. They do not affect the results of queries by block hash when the block is final and known to be on the main chain. explains what these limitations are in detail.
{challenges(block: { number: 8000000 }) {challengeroutcomeapplication {id}}}
This query will return Challenge
entities, and their associated Application
entities, as they existed directly after processing block number 8,000,000.
{challenges(block: { hash: "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c" }) {challengeroutcomeapplication {id}}}
This query will return Challenge
entities, and their associated Application
entities, as they existed directly after processing the block with the given hash.
Fulltext search query fields provide an expressive text search API that can be added to the subgraph schema and customized. Refer to to add fulltext search to your subgraph.
Fulltext search queries have one required field, text
, for supplying search terms. Several special fulltext operators are available to be used in this text
search field.
Fulltext search operators:
Symbol | Operator | Description |
---|---|---|
& | And | For combining multiple search terms into a filter for entities that include all of the provided terms |
| | Or | Queries with multiple search terms separated by the or operator will return all entities with a match from any of the provided terms |
<-> | Follow by | Specify the distance between two words. |
:* | Prefix | Use the prefix search term to find words whose prefix match (2 characters required.) |
Using the or
operator, this query will filter to blog entities with variations of either "anarchism" or "crumpet" in their fulltext fields.
{blogSearch(text: "anarchism | crumpets") {idtitlebodyauthor}}
The follow by
operator specifies a words a specific distance apart in the fulltext documents. The following query will return all blogs with variations of "decentralize" followed by "philosophy"
{blogSearch(text: "decentralized <-> philosophy") {idtitlebodyauthor}}
Combine fulltext operators to make more complex filters. With a pretext search operator combined with a follow by this example query will match all blog entities with words that start with "lou" followed by "music".
{blogSearch(text: "lou:* <-> music") {idtitlebodyauthor}}
Graph Node implements validation of the GraphQL queries it receives using , which is based on the . Queries which fail a validation rule do so with a standard error - visit the to learn more.
The schema of your data source--that is, the entity types, values, and relationships that are available to query--are defined through the .
GraphQL schemas generally define root types for queries
, subscriptions
and mutations
. The Graph only supports queries
. The root Query
type for your subgraph is automatically generated from the GraphQL schema that's included in your subgraph manifest.
Note: Our API does not expose mutations because developers are expected to issue transactions directly against the underlying blockchain from their applications.
All GraphQL types with @entity
directives in your schema will be treated as entities and must have an ID
field.
Note: Currently, all types in your schema must have an @entity
directive. In the future, we will treat types without an @entity
directive as value objects, but this is not yet supported.
All subgraphs have an auto-generated _Meta_
object, which provides access to subgraph metadata. This can be queried as follows:
{_meta(block: { number: 123987 }) {block {numberhashtimestamp}deploymenthasIndexingErrors}}
If a block is provided, the metadata is as of that block, if not the latest indexed block is used. If provided, the block must be after the subgraph's start block, and less than or equal to the most recently indexed block.
deployment
is a unique ID, corresponding to the IPFS CID of the subgraph.yaml
file.
block
provides information about the latest block (taking into account any block constraints passed to _meta
):
- hash: the hash of the block
- number: the block number
- timestamp: the timestamp of the block, if available (this is currently only available for subgraphs indexing EVM networks)
hasIndexingErrors
is a boolean identifying whether the subgraph encountered indexing errors at some past block