Block.one has developed a highly performant blockchain platform, capable of handling a high number of write operations per second for a distributed system. With all of this data, there was a need for an equally performant method to read this data. This is where we focused our attention.

The History of /v1/history

The native history API built into EOSIO’s nodeos, commonly referred to as /v1/history amongst developers, was lacking in both features and functionality. It only offered REST endpoints, could only pull out information at the action-level, and offered little-to-no filtering capabilities. This left a challenge for developers who were trying to use the blockchain to feed data into their applications.

This also presented a set of challenges for node operators, as Block.one deprecated the not-fit-for-use history plugin. This meant that these operators needed to keep up with many changes that arose, keep all of their nodes in sync, and also try to keep up with the new plugins that were starting to take shape. Without any specific recommendations, this left many developers to simply rely on the deprecated, yet officially released, /v1/history API.

Building dfuse

Starting in 2018, dfuse’s team of developers started building to make the life of blockchain developers easier and improve the developer experience. Fast-forward to today, there are over 400 projects being built using the dfuse tech stack and we have open sourced our solution for all developers to use, modify, and build upon. Open sourcing means that we can now share our once-proprietary technology:

  1. dfuse Search – Search the entire transaction history of the blockchain using a simple but powerful query language, with sub-second response times. Developers could filter server-side on fields such as: to, from, signer, account, database changes, etc…) and stream real-time updates reflecting the state of any table on the blockchain, allowing for dynamic user interfaces that update in real time.
  2. dfuse Lifecycle – Submit transactions to the blockchain and receive guaranteed irreversibility in a single call, eliminating the need for developers to write complex code to handle and recover from intricate failure cases.
  3. Granularity – Receive a depth unmatched by any other blockchain service, seeing all database changes, in the specific order they are executed.
  4. Stay fork-aware – No longer a need for each application to write code to handle reorganizations at the tip of the chain. We issue undo responses to keep you up to date of any changes.
  5. Cursor – If you run into a connection issue, easily pick up exactly where you left off, nothing gets dropped.
  6. GraphQL API – Tailor made responses, allowing you to craft the response returned by dfuse to meet your needs
  7. Backwards compatibility – Through version upgrades, dfuse obfuscates any breaking changes to keep your request/response format uniform

*As a side note, our deepmind instrumentation has been merged in the develop branch of EOSIO, which means that it should be released in the next major version release.

Fully Fledged JavaScript Library

By using the dfuse client-js library, much of the complexity has been handled for you. This means that you don’t need to handle reconnection to dfuse, JWT issuance for authentication, cursor caching, among other things. This allows the switch over to dfuse to be done within just a few hours of developer time. This keeps initial costs low, while also saving you on-going maintenance hours for many months to come.

The response format between dfuse and the native history API are slightly different, however it is a negligible difference, handled by minor code tweaks on your part.

Migrating Your Project

When migrating your project, we have two quickstart guides to help you. The first is for JavaScript, which utilizes our client-js library. The second quickstart guide will walk you through utilizing other languages. It should be noted that if you decide not to utilize our client-js library, you’ll need to handle JWT issuance and caching, stream restart and disconnection, and cursor handling.

Migrating /v1/history Code Example

To replace the following /v1/history/get_actions request:

POST /v1/history/get_actions HTTP/1.1 Host: [EOS API] Content-Type: application/json { "account_name": "eosio", "pos": 1, "offset": 1 } 

 

We can use the following dfuse graphql query to get the same results back: Graphiql Link
The query fields have been renamed to match get_actions. The query and params can be found within the link. pos and offset values in a get_actions request can be replaced with block ranges and limits in the dfuse GraphQL request.

The dfuse GraphQL endpoint allows for querying for the specific fields that you need, allowing you to cut down on the amount of code your application will need to filter to retrieve the specific data you require.

Here is a JavaScript Example for both endpoints:
We want to get the account, receiver, and hexData of an action

With v1/history/get_actions

const fetch = require('node-fetch'); const { Headers } = fetch; var myHeaders = new Headers(); myHeaders.append('Content-Type', 'application/json'); var raw = JSON.stringify({ account_name: 'eosio', pos: 1, offset: 1 });   var requestOptions = { method: 'POST', headers: myHeaders, body: raw, redirect: 'follow', };     fetch('[EOS API]/v1/history/get_actions', requestOptions) .then((response) => response.json()) .then((result) => {   // Receive all fields in the result and extract every field from nested data // Also need to loop through actions array and map them to a results array var finalResult = result.actions.map((action) => ({ account: action.action_trace.act.account, receiver: action.action_trace.receiver, trx_id: action.action_trace.trx_id, hex_data: action.action_trace.act.hex_data, })); console.log(finalResult); }) 

 

With dfuse GraphQL

const { createDfuseClient } = require('@dfuse/client'); const client = createDfuseClient({ apiKey: 'YOUR_API_KEY', network: 'mainnet.eos.dfuse.io', });   // Predefine the results you want, and receive only them in your response // Results come in array form that do not need processing const operation = `query { searchTransactionsForward(query: "(auth:eosio OR receiver:eosio)", limit: 1) { results { trace { id matchingActions { account receiver hex_data: hexData } } } } }`;   client .graphql(operation) .then((response) => { const finalResult = [] response.data.searchTransactionsForward.results.forEach((result => { result.trace.matchingActions.forEach((action) => { finalResult.push({ trx_id: result.trace.id, ...action }) }) })) console.log(finalResult) }) 

Join the dfuse Family

Many developers have been well served by dfuse, and we know you’ll be satisfied as well. If you have any questions, please join us in the dfuse Telegram channel. Let us know how your experience went switching from the native history API to dfuse, we’re always excited to hear how the migration went.