Exposing Tezos Data via GraphQL

Querying Tezos data can be a bit challenging. If you don’t have a running indexer, you are left to read from a Tezos node directly using the RPC interface. This interface can change with every protocol version and indeed past protocol updates did come with breaking changes. This means you potentially need to parse data differently depending on the block you are trying to access.

We are proposing a GraphQL interface that will allow accessing data in a friendlier and more flexible way. One nice benefit is a cushy layer of backward and forward compatibility that lets data consumers worry less about potential breaking changes. This compatibility layer can (in theory) be maintained as long as is reasonable and technically feasible.

We have launched a new service at TezosLive.io, that exposes an experimental GraphQL interface which everybody can use. You can read more about the publicly available endpoints on Medium. The source code is available on our GitHub page.

What is GraphQL

GraphQL is an open source query language for fetching data with focus on efficiency, flexibility and interoperability. It is especially useful for hierarchical data or data with frequent relations between different objects.

When applied to the Tezos blockchain, it allows for fetching ranges of blocks, specifying exactly the data needed from each block, applying filters, paging, and more.

Queries

A basic query that will fetch information about the head block will look like

{ block { hash protocol metadata { level { level cycle } } } }

This query can return something like

{ "data": { "block": { "hash": "BKvPTjpFiLZLZ6Rf3gr2peUq6P9hPU3KUtMTTQMdYYhuJmmhwQd", "protocol": "PsCARTHAGazKbHtnKfLzQg3kms52kSRpgnDY982a9oYsSXRLQEb", "metadata": { "level": { "level": 893639, "cycle": 218 } } } } }

As you can see the JSON structure strictly follows what we asked for. When using GraphQL, you only get what you specifically requested and nothing more.

Collections

Collections of things can be queried easily. For example to fetch transactions in the block of a given hash you can run:

{ block(block: "BMRSZMEKqCk3a6wE2Nj2s2exF3W5rDwEkpDXA4VxUjsKokaNK4q") { transactions { source destination amount } } }

This query will yield you the response:

{ "data": { "block": { "transactions": [ { "source": "tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m", "destination": "tz1eSKjwW1vEZZR9C2X2dqfPVrkBLTMwEt4j", "amount": "114516362" }, { "source": "tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m", "destination": "tz1U7HrhpgbCyTq8Y7myPc8tZp3iQix431tz", "amount": "28191980" }, { "source": "tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m", "destination": "tz1cDq7pjwpddUrL6DredV6vUrnq58GLe6nm", "amount": "4495000" } ] } } }

Transactions are returned as an array. Again, the items only contain the properties we requested.

Paging

We can also request ranges of blocks. The following example will fetch the last 5 blocks and the reveals that happened in those blocks:

{ blocks(count: 5) { reveals { source fee } } }

This query will yield something like:

{ "data": { "blocks": [ { "hash": "BMQfyb5Mb2k2de127NvxDwi7DH1tYnDvAAQh51Ydd19Uj2QJBeR", "reveals": [] }, { "hash": "BLo8xNbhxaVnR6SRKLwsFenvXsUCQcRRr5HGq2ze6WshfGDvCEZ", "reveals": [ { "source": "tz1ih9QLqVM1TXv1EEi4Wprxp8LfFMcYgL8H", "fee": "1300" } ] }, { "hash": "BMDygq7zBnWss7sCFdHdfNnQ6MGzXW7YtAePFCjjAB1jNxuy7GM", "reveals": [ { "source": "tz1YvPms29CezyN4K4ZWmNNjHkpQ9h4k7VQB", "fee": "1268" }, { "source": "tz2EwrgpokE9iC5faCAZxJbuYx3Qk1BHdtkJ", "fee": "1420" } ] } ] } }

The “blocks” field will accept a set of arguments that should cover common paging scenarios:

from - an inclusive lower limit of the range. You can use all common notations. E.g. "head" , "head~17" (relative level), "975623" (absolute level) or a direct hash

- an inclusive lower limit of the range. You can use all common notations. E.g. , (relative level), (absolute level) or a direct hash to - an inclusive upper limit of the range with the same options as from

- an inclusive upper limit of the range with the same options as count - the maximum elements to fetch. If there are less than count elements, only count elements will be returned

Examples of queries that you can run:

blocks(to: "head~2", count: 4) - fetch 4 past blocks, skipping the 2 most recent blocks

- fetch 4 past blocks, skipping the 2 most recent blocks blocks(from: "800000", to: "800009") - fetch the blocks from level 800000 to level 800009 (inclusive on both ends)

- fetch the blocks from level 800000 to level 800009 (inclusive on both ends) blocks(from: "[somehash]", count: 3) - fetch 3 blocks from “[somehash]” onwards

Fetching Contracts

Fetching smart contract details and storage can be done easily by supplying the address:

{ block { contract(address: "KT1TMV3aBNWoAgJmKoY4nGZVWGGZNB1udnJw") { balance storage_decoded } } }

Will return

{ "data": { "block": { "contract": { "balance": "1000", "storage_decoded": { "coldOwnerKey": "edpkusYkvo99KWxnJp5ve6UGnrnBjYVX5HJp7WSd2y3CRZU1chaQNL", "feeAmount": "200000", "ownerKey": "tz1LtZQBeQsGqPSdBtoT8kYb1YBUQh2NRz2w", "userToWallet": "1954" } } } } }

The Schema and TZIP-14

The full GraphQL schema is still a work in progress. You can check out the current version in GitHub. We are now formalizing this idea under TZIP-14 and you should soon be able to see a draft version in the TZIP repository.

We would like to invite the community to give us feedback and ideas for improvements. With your help the GraphQL interface can become useful to data consumers and developers who need a friendly way to access the Tezos blockchain.