Quick Start: JavaScript
In this guide we will show you how to create a basic setup so that you can benefit from the dfuse GraphQL API under one of the supported JavaScript environments:
- Browser through a bundler like Webpack (includes Create React App projects) (
Bundler
code tab) - Browser standalone (
Browser
code tab) - Node.js server (
Node.js
code tab)
Note
While not yet in the supported list, you should be able to use the example under a React Native environment.All examples uses ES6 syntax with await/async
keywords, using import
keywords on Bundler
and Browser
environments while using the require
syntax on Node.js environment. However, the library compiles itself to down to ES5 features, so we support older ES5 compliant browsers that are not compatible with ES6 features (IE 11 for example).
We assume the commands below are being performed in an empty project folder. To quickly start an empty project:
mkdir -p example-dfuse-javascript
cd example-dfuse-javascript
npm init -y
mkdir -p example-dfuse-javascript
cd example-dfuse-javascript
yarn init -y
1. Get a dfuse API Key
Get an API key
- Create your account on dfuse.eosnation.io
-
Click “Create New Key” and give it a name, a category. In the case of a
web
key give it an “Origin” value.
2. Adding the Client Library
The simplest way to get started with dfuse and JavaScript/TypeScript development is to use the dfuse JS client library with TypeScript typings.
Here are a few of its key features:
- Handles API token issuance
- Refreshes your API token upon expiration
- Automatically reconnects if the connection closes
- Supports
Browsers
andNode.js
environments
You can add it to your project using Yarn or NPM.
npm install @dfuse/client
yarn add @dfuse/client
Node.js Extra Steps
If you are targeting the Node.js
environment, a few extra steps are required to be able to use
the @dfuse/client library. The library relies on the node-fetch
package for HTTP requests
and on the ws
package for WebSocket
connections.
npm install node-fetch ws
yarn add node-fetch ws
Once installed, prior to calling anything else, ensure that global.fetch
and global.WebSocket
are set in the global scope.
Important
This is required only in a Node.js environment. When targeting aBrowser
environment (in
standalone HTML or through a bundler, @dfuse/client library automatically uses fetch
and WebSocket
objects provided by the browser).
global.fetch = require('node-fetch')
global.WebSocket = require('ws')
Note
You prefer to not pollute the global scope? Check Node.js Configuration Example to see how you can pass the options directly when instantiating the client instead of polluting the global scope.3. Create the client
With the initial setup complete, you can start coding. The first thing we will do is initialize the dfuse client using the API key you created in the first step and the network you want to connect to.
Valid networks can be found at Available EOSIO Networks (Endpoints)
const { createDfuseClient } = require('@dfuse/client');
const client = createDfuseClient({
apiKey: process.env.DFUSE_API_KEY,
network: 'eos.dfuse.eosnation.io'
});
import { createDfuseClient } from "@dfuse/client"
const client = createDfuseClient({
apiKey: process.env.DFUSE_API_KEY,
network: "eos.dfuse.eosnation.io",
})
<head>
<style> li { font-family: monospace; margin: 0.15; }</style>
<script src="https://unpkg.com/@dfuse/client"></script>
<script>
const client = dfuseClient.createDfuseClient({
// Replace 'web_abcdef12345678900000000000' with your own API key!
apiKey: 'web_abcdef12345678900000000000',
network: 'eos.dfuse.eosnation.io'
})
</script>
</head>
4. Stream your first results
Let’s first define the GraphQL operation, as a string, that we will use to perform GraphQL subscription. This element tells the backend server what fields to return to you, you get to choose and pick only what you are interested in.
Note
Want to inspect the full set of available fields you can retrieve?
// You must use a $cursor
variable so stream starts back at last marked cursor on reconnect!
const operation = subscription($cursor: String!) { </span><span style="color:#e6db74"> searchTransactionsForward(query:"receiver:eosio.token action:transfer -data.quantity:'0.0001 EOS'", cursor: $cursor) { </span><span style="color:#e6db74"> undo cursor </span><span style="color:#e6db74"> trace { id matchingActions { json } } </span><span style="color:#e6db74"> } </span><span style="color:#e6db74">}
;
// You must use a $cursor
variable so stream starts back at last marked cursor on reconnect!
const operation = subscription($cursor: String!) { </span><span style="color:#e6db74"> searchTransactionsForward(query:"receiver:eosio.token action:transfer -data.quantity:'0.0001 EOS'", cursor: $cursor) { </span><span style="color:#e6db74"> undo cursor </span><span style="color:#e6db74"> trace { id matchingActions { json } } </span><span style="color:#e6db74"> } </span><span style="color:#e6db74">}
<script>
// You must use a $cursor
variable so stream starts back at last marked cursor on reconnect!
const operation = subscription($cursor: String!) { </span><span style="color:#e6db74"> searchTransactionsForward(query:"receiver:eosio.token action:transfer -data.quantity:'0.0001 EOS'", cursor: $cursor) { </span><span style="color:#e6db74"> undo cursor </span><span style="color:#e6db74"> trace { id matchingActions { json } } </span><span style="color:#e6db74"> } </span><span style="color:#e6db74">}
</script>
Next, you create the GraphQL subscription to stream transfers as they come. You will use the searchTransactionsForward
operation, with the "receiver:eosio.token action:transfer -data.quantity:'0.0001 EOS'"
query (see Search Terms for EOSIO). This basically means, give me all transactions containing one or more
actions named transfer
executed by the eosio.token
account for which the quantity
field of the action is not 0.0001 EOS
.
You can combine the dfuse client instance we created in step 3 with the GraphQL document we defined above in
a main
function:
async function main() {
const stream = await client.graphql(operation, message => {
if (message.type === 'data') {
const {
undo,
cursor,
trace: { id, matchingActions }
} = message.data.searchTransactionsForward;
matchingActions.forEach(({ json: { from, to, quantity } }) => {
console.log(
Transfer </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">from</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> -> </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">to</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> [</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">quantity</span><span style="color:#e6db74">}</span><span style="color:#e6db74">]</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">undo</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">' REVERTED'</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">''</span><span style="color:#e6db74">}</span><span style="color:#e6db74">
);
});
<span style="color:#75715e">// Mark stream at cursor location, on re-connect, we will start back at cursor
stream.mark({ cursor });
}
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'error'</span>) {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'An error occurred'</span>, <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">errors</span>, <span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">terminal</span>);
}
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">'complete'</span>) {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Completed'</span>);
}
});
// Waits until the stream completes, or forever
await stream.join();
await client.release();
}
// You would normally use your framework entry point and render using components,
// we are using pure HTML manipulation for sake of example simplicity.
async function main() {
const stream = await client.graphql(operation, (message) => {
if (message.type === "data") {
const { undo, cursor, trace: { id, matchingActions }} = message.data.searchTransactionsForward
matchingActions.forEach(({ json: { from, to, quantity } }) => {
const paragraphNode = document.createElement("li")
paragraphNode.innerText = Transfer </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">from</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> -> </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">to</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> [</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">quantity</span><span style="color:#e6db74">}</span><span style="color:#e6db74">]</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">undo</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">" REVERTED"</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">""</span><span style="color:#e6db74">}</span><span style="color:#e6db74">
document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">prepend</span>(<span style="color:#a6e22e">paragraphNode</span>)
})
<span style="color:#75715e">// Mark stream at cursor location, on re-connect, we will start back at cursor
stream.mark({ cursor })
}
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">"error"</span>) {
<span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">errors</span>, <span style="color:#a6e22e">terminal</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">message</span>
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">paragraphNode</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">"li"</span>)
<span style="color:#a6e22e">paragraphNode</span>.<span style="color:#a6e22e">innerText</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`An error occurred </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>({ <span style="color:#a6e22e">errors</span>, <span style="color:#a6e22e">terminal</span> <span style="color:#e6db74">}</span><span style="color:#e6db74">)}`</span>
document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">prepend</span>(<span style="color:#a6e22e">paragraphNode</span>)
}
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">"complete"</span>) {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">paragraphNode</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">"li"</span>)
<span style="color:#a6e22e">paragraphNode</span>.<span style="color:#a6e22e">innerText</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">"Completed"</span>
document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">prepend</span>(<span style="color:#a6e22e">paragraphNode</span>)
}
})
// Waits until the stream completes, or forever
await stream.join()
await client.release()
}
<script>
async function main() {
const stream = await client.graphql(operation, (message) => {
if (message.type === "data") {
const { undo, cursor, trace: { id, matchingActions }} = message.data.searchTransactionsForward
matchingActions.forEach(({ json: { from, to, quantity } }) => {
const paragraphNode = document.createElement("li")
paragraphNode.innerText = Transfer </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">from</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> -> </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">to</span><span style="color:#e6db74">}</span><span style="color:#e6db74"> [</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">quantity</span><span style="color:#e6db74">}</span><span style="color:#e6db74">]</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">undo</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">" REVERTED"</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">""</span><span style="color:#e6db74">}</span><span style="color:#e6db74">
document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">prepend</span>(<span style="color:#a6e22e">paragraphNode</span>)
})
<span style="color:#75715e">// Mark stream at cursor location, on re-connect, we will start back at cursor
stream.mark({ cursor })
}
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">"error"</span>) {
<span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">errors</span>, <span style="color:#a6e22e">terminal</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">message</span>
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">paragraphNode</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">"li"</span>)
<span style="color:#a6e22e">paragraphNode</span>.<span style="color:#a6e22e">innerText</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`An error occurred </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>({ <span style="color:#a6e22e">errors</span>, <span style="color:#a6e22e">terminal</span> <span style="color:#e6db74">}</span><span style="color:#e6db74">)}`</span>
document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">prepend</span>(<span style="color:#a6e22e">paragraphNode</span>)
}
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">message</span>.<span style="color:#a6e22e">type</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">"complete"</span>) {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">paragraphNode</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">createElement</span>(<span style="color:#e6db74">"li"</span>)
<span style="color:#a6e22e">paragraphNode</span>.<span style="color:#a6e22e">innerText</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">"Completed"</span>
document.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">prepend</span>(<span style="color:#a6e22e">paragraphNode</span>)
}
})
// Waits until the stream completes, or forever
await stream.join()
await client.release()
}
</script>
The function passed as the 2nd parameter to client.graphql()
will be called every time a new result is returned
by the API. And here is a sample of the prints you will receive as a result of executing the streaming operation
above:
Transfer eosbetdice11 -> eosbetbank11 [0.0500 EOS]
Transfer newdexpublic -> gq4tcnrwhege [2.8604 EOS]
Transfer wpwpwp222222 -> eosioeosios3 [20.0000 EOS]
Transfer wallet.bg -> bulls.bg [0.9000 EOS]
Transfer bluebetproxy -> bluebetbulls [0.6000 EOS]
...
5. Full Working Examples
Here the small glue code containing the main
function, imports and other helper functions to run the example:
main().catch(error => console.log('Unexpected error', error));
main().catch((error) => document.body.innerHTML = <p></span><span style="color:#e6db74">${</span><span style="color:#a6e22e">error</span><span style="color:#e6db74">}</span><span style="color:#e6db74"></p>
)
<script>
main().catch((error) => document.body.innerHTML = <p></span><span style="color:#e6db74">${</span><span style="color:#a6e22e">error</span><span style="color:#e6db74">}</span><span style="color:#e6db74"></p>
)
</script>
git clone https://github.com/EOS-Nation/dfuse-docs
cd dfuse-docs/quickstarts/javascript/node
npm install
# Replace 'server_abcdef12345678900000000000' with your own API key!
DFUSE_API_KEY=server_abcdef12345678900000000000 node index.eosio.js
git clone https://github.com/EOS-Nation/dfuse-docs
cd dfuse-docs/quickstarts/javascript/bundler
npm install
# Replace 'web_abcdef12345678900000000000' with your own API key!
DFUSE_API_KEY=web_abcdef12345678900000000000 npm run build:eosio
# Open index.eosio.html
directly in your favorite Browser
open index.eosio.html # Mac
xdg-open index.eosio.html # Ubuntu
start index.eosio.thml # Windows
git clone https://github.com/EOS-Nation/dfuse-docs
cd dfuse-docs/quickstarts/javascript/browser
# Manually edit index.eosio.html changing web_abcdef12345678900000000000
with your own API key
# Open index.eosio.html
directly in your favorite Browser
open index.eosio.html # Mac
xdg-open index.eosio.html # Ubuntu
start index.eosio.thml # Windows