This example demonstrates how to use the dfuse GraphQL APIs in a React application to livestream the average execution rates for the top actions. A live demo is available here.
The following assumes you have yarn installed on your computer.
git clone https://github.com/dfuse-io/example-stream-action-rates.git
cd example-stream-action-rates
yarn install
yarn start
# Automatically open ups a browser pointing to `localhost:3000`
First, head to the dfuse self-service API management portal (https://app.dfuse.io). After signing up, you will be able to create long-term API keys.
We use the Apollo Client as well as the @dfuse/client library to connect to the GraphQL server. You can install the Apollo Client and other required packages via:
yarn add @dfuse/client apollo-boost apollo-client apollo-link-ws graphql react-apollo subscriptions-transport-ws
The easiest way to talk to the dfuse API is to use the @dfuse/client library which handles all of the heavy work of retrieving an API token, persist it to disk to avoid hitting rate limits on API token issuance, and ensures the token is always fresh.
In our example, we instantiate the Apollo Client to leverage the power of @dfuse/client library and let it handle API token management:
See src/client.ts
import { WebSocketLink } from "apollo-link-ws";
import ApolloClient from "apollo-client/ApolloClient";
import { InMemoryCache } from "apollo-cache-inmemory";
import { createDfuseClient } from "@dfuse/client";
const dfuseClient = createDfuseClient({
network: "mainnet",
apiKey: "YOUR_API_KEY_HERE" // <--- Change this value for your own API Key!
})
const wsLink = new WebSocketLink({
uri: dfuseClient.endpoints.graphqlStreamUrl,
options: {
lazy: true,
reconnect: true,
connectionParams: async () => {
const apiToken = await dfuseClient.getTokenInfo()
return {
Authorization: `Bearer ${apiToken.token}`
};
}
}
});
export const apolloClient = new ApolloClient({
link: wsLink,
cache: new InMemoryCache()
});
- The dfuse GraphQL documentation can be found here
- If you are not familiar with GraphQL already, take a look at Introduction to GraphQL
- To help you construct your query and access our API documentation you can use GraphiQL "A graphical interactive in-browser GraphQL IDE." https://mainnet.eos.dfuse.io/graphiql/
We use the gql function to build our subscription query:
See src/graphql.ts
import { gql } from "apollo-boost";
export const subscribeTransactions = gql`
fragment actionTracesFragment on ActionTrace {
account
receiver
name
}
subscription subscribeTransactions($cursor: String, $lowBlockNum: Int64) {
searchTransactionsForward(
query: "action:transfer"
lowBlockNum: $lowBlockNum
cursor: $cursor
) {
cursor
undo
trace {
id
status
block {
id
num
timestamp
}
executedActions {
...actionTracesFragment
}
}
}
}
`;
Apollo provides an ApolloProvider
component to link the Apollo Client to the React application. Using
the subscription query is as simple as passing it to the Subscription
component (read
Apollo documentation for more details).
See src/App.tsx
export class App extends Component {
...
onSubscriptionData = ({ client, subscriptionData }: any) => {
const result = (subscriptionData.data.searchTransactionsForward) as SearchResult;
console.log(result)
};
render() {
return (
<ApolloProvider client={apolloClient}>
<Subscription
subscription={subscribeTransactions}
variables={{ cursor: "", lowBlockNum: -100 }}
onSubscriptionData={this.onSubscriptionData}
/>
</ApolloProvider>
)
}
}
The response from the server is parsed and fed into an actionsMap
hash to hold the rates for each action contract/name pair:
See src/models.ts
See src/App.tsx#105
onSubscriptionData = ({ client, subscriptionData }: any) => {
const { undo, trace } = (subscriptionData.data.searchTransactionsForward) as SearchResult;
trace.executedActions.forEach((action) => {
const key = `${action.account}:${action.name}`;
const increment = undo ? -1 : 1;
this.actionsMap[key] = (this.actionsMap[key] || 0) + increment;
});
}
The snippet above contains an undo
parameter (returned inside the payload of the subscription response),
that parameter handles micro-forks inside the chain and the counter is decremented if it is set to true
.
You can read more about navigating forks in our documentation.