GraphQL Semantics
Getting Started with GraphQL
The GraphQL API offers significantly more flexibility than the REST API. The ability to define precisely the data you want—and only the data you want—is a powerful advantage over the REST API endpoints. GraphQL lets you replace multiple REST requests with a single call to fetch the data you specify.
The GraphQL API offers two types of requests, Queries and Subscriptions, allowing you to build flexible real-time applications.
You can find the dfuse GraphQL endpoints within the docs of the chain you are developing on:
Queries
Query the block hash that contains a specific transaction:
query {
transaction(hash: "0x1798aefe0fe6f15abcaed3901474c1bd4303ba0fafe32d232e5121e29d63841e") {
block {
hash
}
}
}
{
"data": {
"transaction": {
"block": {
"hash": "0x3f59be0a3a65b9eb8f30683d69267eff461fc984ed2ebf9c8c7fda0e0bd8b2d1"
}
}
}
}
The simplest type of call you can make is a GraphQL Query. This is a single network request that will allow you to query the API.
For example, we can use this to find the block hash that contains a specific transaction.
Try it on GraphiQLSubscriptions
Stream all transactions to an account, in real-time:
subscription {
searchTransactions(indexName:CALLS query: "to:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", lowBlockNum: -1000) {
node {
hash
}
}
}
{
"searchTransactions": {
"node": {
"hash": "0xa900fdfe012fba52bb9caf5de290fbc762e6f31f8222b13fa4fa2a58c3ae02a5"
}
}
}
A more advanced method is the GraphQL Subscription. This gives you access to a stream of transactions, essential for all kinds of real-time applications.
For example, we can use this to obtain the hash of all transfers happening on the chain, in real-time.
Try it on GraphiQLPaginated Queries
Query the most recent transactions to an address with 3 documents per page:
query {
searchTransactions(indexName:CALLS query: "to:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", sort: DESC, limit: 3) {
pageInfo {
endCursor
}
edges {
node {
hash
}
}
}
}
{
"data": {
"searchTransactions": {
"pageInfo": {
"endCursor": "dlzNRn_o7zfQkFqyJa4g0Pe7LJMwBFpmVAHkLhgVjd_z83fE2p7yBGMkYR3Sw6-m1RzvQl351tvIFC198MBXuNa-lb8yviBtQCkskoHs-bS-evahPgJOJLJlVbiMMdHbUjveZV7_eQ=="
},
"edges": [
{
"node": {
"hash": "0x6ac069cd5baeda86c768d7ec3228db15116c9014cce2f8fb73ca476c15726487"
}
},
{
"node": {
"hash": "0x9f9cf19b37a25a16253cdce7782db475de9742cefe51f3f33f8f67032b1cf986"
}
},
{
"node": {
"hash": "0x1de49b2ea41b71a9e684bd4c8da16d276484aeeb77994e9bb341978f02dc8da3"
}
}
]
}
}
}
The GraphQL API provides cursors to enable pagination. With every response, you can receive a cursor that is a reference to a specific document. By providing this cursor in a subsequent request, the API will pick up where you left off.
Besides pagination, cursors are instrumental when using subscriptions to deal with network disconnections. By using the cursor of your last successful request, you can reconnect and continue streaming without missing any documents.
Try it on GraphiQLNavigating Forks
Stream all transactions to an account, keeping an eye on forks:
subscription {
searchTransactions(indexName:CALLS query: "to:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d", lowBlockNum: -1000) {
undo
node {
hash
}
}
}
{
"searchTransactions": {
"undo": false,
"node": {
"hash": "0xaf0c1606c9ae19020989c0be9b613fcd023e0f7906ad463a1e2d12b97d2c7edf"
}
}
}
When dealing with documents that were very recently added to a blockchain, there is a risk that the block this document is currently in gets forked out.
When this happens, you can be notified by retrieving the undo
property of the API. It is your responsibility to ensure that you respond properly to transactions being forked out of the chain.
Important
Never forget to retrieve theundo
property, as streaming results will re-send the matching transactions with undo:true
upon micro-fork resolution.
Transports
The dfuse GraphQL endpoints support the following transports:
-
POST
REST calls to/graphql
, for GraphQL Queries only. -
The Apollo Subscriptions Transport protocol , based on WebSocket
-
GraphQL over gRPC (for server-to-server streaming communications):
-
Method:
dfuse.eosio.v1.GraphQL/Execute
-
The endpoints provide reflection to get the
.proto
schemas
-
GraphQL over REST
You can run GraphQL queries by querying the /graphql
path of dfuse endpoints.
Apollo Subscription Transport
dfuse GraphQL implements subscriptions using the Apollo Websocket Transport protocol, version 0.9.16 .
In the browser, you can use the apollo-client
npm library to connect and read responses. It uses WebSocket for Subscriptions and can also use it for Queries.
Tip
See the Apollo Client Introduction for more details.GraphQL over gRPC
Using grpcurl
List available gRPC methods with:
grpcurl eos.dfuse.eosnation.io:9000 list
Stream live search query responses:
echo '{"query": "subscription { searchTransactionsForward(limit: 10, query: \"status:executed\") { cursor undo trace { id matchingActions { receiver account name json } } } }"}' \
| grpcurl -H "Authorization: Bearer $DFUSE_TOKEN" -d @ \
eos.dfuse.eosnation.io:9000 dfuse.eosio.v1.GraphQL/Execute
For a nice output, install jq
and pipe the previous command into:
jq '.data | fromjson | .data.searchTransactionsForward.trace'
# or
jq -r .data
- Install grpcurl
, a simple
curl
-like program to communicate via gRPC. - Make a GraphQL request, sending along a valid Authorization token.
- When viewing the output, you can find the GraphQL response wrapped as a string in the gRPC
data
field.
Launch grpcui
:
grpcui -port 6000 eos.dfuse.eosnation.io:9000
Using grpcui
- Install grpcui .
- Open http://localhost:6000 and explore the interface.
- Add the
authorization
header in the interface, in the format:Bearer TOKEN
whereTOKEN
is a valid JWT.
Note
grpcui
doesn’t handle streaming responses properly; it jams until the subscription is terminated. To view streaming search results, use grpcurl
instead as explained just above.
Searching Through GraphQL
The dfuse Search engine exposed through the GraphQL endpoint has a few peculiarities that are worthy to note here:
- The cursor property is chain-wide, and is returned with each result, so you can pick up where you left off at each transaction, and not worry that a block has been partially applied.
- It navigates forks in a slightly different way than the WebSocket
get_table_rows
. See Navigating Forks. - You can do a backward search to get recent transactions up to a limit, and then use the first cursor from those results to do a forward search on the same query, and listen to real-time events, all while navigating forks. Make sure you keep track of the
undo
property in forward searches.
API Reference
This section contains subscriptions and queries that can be performed against our GraphQL interface.
The best way to explore the GraphQL schemas, available subscriptions & queries as well as all arguments is to use the dfuse GraphiQL web page we provide for the different endpoints.
The GraphQL schema is fully documented and should answer most of your questions
regarding the data it serves. Within GraphiQL, simply place your
cursor somewhere and press Ctrl+<Space>
to see completion possibilities for the current location.
Subscriptions
searchTransactionsForward
Search the blockchain forward for transaction execution traces based on the given query.
Warning
Always consider the undo field in forward searches, which signal that the matching element was in fact removed from the chain due to a chain reorganization.searchTransactionsBackward
Search the blockchain backward for transaction execution traces based on the given query.
Note
The undo field is not used in a backward search.Queries
searchTransactionsForward
Search the blockchain forward for transaction execution traces based on the given query. When the returned cursor is empty, it means you have reached the end of the specified block range.
Warning
Always consider the undo field in forward searches, which signal that the matching element was in fact REMOVED from the chain because of a chain reorganization.searchTransactionsBackward
Search the blockchain backward for transaction execution traces based on the given query. When the returned cursor is empty, it means you have reached the end of the specified block range.
Try it on GraphiQLblockIDByTime
Return the block ID found around the given time
, based on the comparator provided.
Sample Queries
To get you started, here are a few sample queries and how to read them.
Streaming Transactions
The following query (try it on GraphiQL ):
subscription
prefix - Issues a GraphQL subscription call (streaming results).query:"receiver:eosio.token account:eosio.token action:transfer"
- The query utilizing the dfuse Search Query Language that you would like responses to match. This query requests responses fortransfer
actions on theeosio.token
smart contract.lowBlockNum: 0
- Defaults to the HEAD of the chain where it then begins listening for new real-time blocks that match the query.limit:20
- The amount of matched responses that should be accumulated before returning a payload. Once returned, the subscription will be closed.matchingActions
- Retrieve the matching actions. Note there could be many in a single transaction.creatorAction
- For each matching action, we also retrieve the action that caused this transfer, if any. If a token transfer was initiated by another smart contract,creatorAction
will be non-null, and will point to the action which caused the creation (see the GraphQL schema for full details).
subscription {
searchTransactionsForward(
query:"receiver:eosio.token account:eosio.token action:transfer",
lowBlockNum:0,
limit:20,
) {
undo
cursor
trace {
id
matchingActions {
receiver
account
name
json
creatorAction {
receiver
account
name
json
}
}
}
}
}
{
"searchTransactionsForward": {
"undo": false,
"cursor": "BKg1cvtNtiumthn4ayUAcfe7IZI9AlpmUgnvKhJFhYinoSHG2pv1AmQmYRjXlKj120frHl6ri4zPQn8p9pJRvNbixrhm6HRpEC8km4nn_bW5fvrxMA4fJbw3C-CJNN-JXj2DZgivc-A=",
"trace": {
"id": "7f7b51d42d9a58f461b7a88415a7cf84cc1e346fc27c022267e01dcf4c437de8",
"matchingActions": [
{
"receiver": "eosio.token",
"account": "eosio.token",
"name": "transfer",
"json": {
"from": "trustdicewin",
"to": "antoinewu123",
"quantity": "0.0005 EOS",
"memo": "antoinewu123-Faucet from the ...Platform! ..."
},
"creatorAction": {
"receiver": "trustdicewin",
"account": "trustdicewin",
"name": "coinbox",
"json": {
"memo": "antoinewu123-antoinewu123-EOS-14fb7........."
}
}
}
]
}
}
}
Multiple GraphQL queries in one request:
The following query (try it on GraphiQL ):
- Issues a GraphQL query that retrieves responses for two queries at once
- Each querying the block ID and number less than or equal to the date specified in
time
. - It remaps the result to
start
andend
respectively.
{
start:blockIDByTime(time: "2019-01-01T00:00:00Z") {
time
num
id
}
end:blockIDByTime(time: "2019-02-01T00:00:00Z") {
time
num
id
}
}
{
"data": {
"start": {
"time": "2019-01-01T00:00:00Z",
"num": 35058781,
"id": "0216f45d4cf4dd026436e270a38d4a6f4b8ff6b66c51169959c7d15cc546c454"
},
"end": {
"time": "2019-02-01T00:00:00Z",
"num": 40401308,
"id": "0268799ce334f320485b80dd632db5168c6d894a289b82dc721e029a3a50038c"
}
}
}
Note
Batched operations are always as slow as the slowest operation in the batch.