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 GraphiQL

Subscriptions

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 GraphiQL

Paginated 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 GraphiQL

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.

Try it on GraphiQL

Important

Never forget to retrieve the undo 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
  1. Install grpcurl , a simple curl-like program to communicate via gRPC.
  2. Make a GraphQL request, sending along a valid Authorization token.
  3. 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

  1. Install grpcui .
  2. Open http://localhost:6000 and explore the interface.
  3. Add the authorization header in the interface, in the format: Bearer TOKEN where TOKEN 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:

  1. 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.
  2. It navigates forks in a slightly different way than the WebSocket get_table_rows. See Navigating Forks.
  3. 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.
Try it on GraphiQL

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.
Try it on GraphiQL

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.
Try it on GraphiQL

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 GraphiQL

blockIDByTime

Return the block ID found around the given time, based on the comparator provided.

Try it on GraphiQL Sample

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 for transfer actions on the eosio.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 and end 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.