Getting Started with Other Languages

The code from the examples on this page lives in the quickstarts folder of this docs GitHub repository .

1. Get a dfuse API Key

Get an API key

  1. Create your account on https://app.dfuse.io
  2. Click “Create New Key” and give it a name, a category. In the case of a web key give it an “Origin” value.
See Authentication for further details

Initiate your project

Generate the client stub and dependencies for your language

dfuse exposes its data through a GraphQL over gRPC interface. The protobuf files are in this GitHub repository .

A lot of languages provide tools to generate client stubs from protobuf files, as you can find in the official gRPC documentation .


git clone https://github.com/dfuse-io/graphql-over-grpc.git
mkdir my-project
cd my-project

# You can install protoc by following those instructions
# https://grpc.io/docs/quickstart/go.html#before-you-begin

protoc -I ../graphql-over-grpc ../graphql-over-grpc/graphql/graphql.proto --go_out=plugins=grpc:.

go mod init my-project
touch main.go


python -m pip install grpcio-tools --ignore-installed

git clone https://github.com/dfuse-io/graphql-over-grpc.git
mkdir my-project
cd my-project

python -m grpc_tools.protoc -I ../graphql-over-grpc --python_out=. --grpc_python_out=. graphql/graphql.proto

touch main.py


# On MacOS
brew install grpcurl

# On Linux/Windows
go get github.com/fullstorydev/grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl

# Download from Git
git clone https://github.com/dfuse-io/graphql-over-grpc

# Download from a Zip archive
curl -sLO https://github.com/dfuse-io/graphql-over-grpc/archive/master.zip
unzip -q master.zip

# Generate your code
cd graphql-over-grpc
protoc graphql/graphql.proto # add your language-specific flags here

Start by adding this code to your “main”


package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    pb "go/graphql"
    "io/ioutil"
    "net/http"
    "os"
    "time"

    "github.com/tidwall/gjson"
    "golang.org/x/oauth2"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/credentials/oauth"
)

func main() {

    dfuseAPIKey := os.Getenv("DFUSE_API_KEY")
    if dfuseAPIKey == "" || dfuseAPIKey == "your dfuse api key here" {
        panic("you must specify a DFUSE_API_KEY environment variable")
    }

    token, _, err := getToken(dfuseAPIKey)
    panicIfError(err)

    client := createClient("mainnet.eth.dfuse.io:443", token)

    streamEthereum(context.Background(), client)
}

func panicIfError(err error) {
    if err != nil {
        panic(err)
    }
}

try:
    # python3
    from http.client import HTTPSConnection
except ImportError:
    # python2
    from httplib import HTTPSConnection

import grpc
import json
import os
import ssl
import sys

from graphql import graphql_pb2_grpc
from graphql.graphql_pb2 import Request

#
# Code from getting started will go here ...
#

dfuse_api_key = os.environ.get("DFUSE_API_KEY")
if dfuse_api_key == None or dfuse_api_key == 'your dfuse api key here':
    raise Exception('you must specify a DFUSE_API_KEY environment variable')

token = get_token(dfuse_api_key)
# The client can be re-used for all requests, cache it at the appropriate level

client = create_client(token, 'mainnet.eth.dfuse.io:443')
stream_ethereum(client)

The code from the examples on this page lives in the quickstarts folder of this docs GitHub repository .

Generate a JWT from your API key

The JWT is a token with a short expiration period, used to communicate with dfuse services. You will have to implement token caching and manage renewal upon expiration. See Authentication for details.


func getToken(apiKey string) (token string, expiration time.Time, err error) {
    reqBody := bytes.NewBuffer([]byte(fmt.Sprintf(`{"api_key":"%s"}`, apiKey)))
    resp, err := http.Post("https://auth.dfuse.io/v1/auth/issue", "application/json", reqBody)
    if err != nil {
        err = fmt.Errorf("unable to obtain token: %s", err)
        return
    }

    if resp.StatusCode != 200 {
        err = fmt.Errorf("unable to obtain token, status not 200, got %d: %s", resp.StatusCode, reqBody.String())
        return
    }

    if body, err := ioutil.ReadAll(resp.Body); err == nil {
        token = gjson.GetBytes(body, "token").String()
        expiration = time.Unix(gjson.GetBytes(body, "expires_at").Int(), 0)
    }
    return
}

def get_token(api_key):
    connection = HTTPSConnection("auth.dfuse.io")
    connection.request('POST', '/v1/auth/issue', json.dumps({"api_key": api_key}), {'Content-type': 'application/json'})
    response = connection.getresponse()

    if response.status != 200:
        raise Exception(" Status: %s reason: %s" % (response.status, response.reason))

    token = json.loads(response.read().decode())['token']
    connection.close()

    return token

curl https://auth.dfuse.io/v1/auth/issue -s \
  --data-binary '{"api_key":"server_abcdef12345678900000000000"}'

Create the client

We can now define the client creation code. The client can be re-used across all the requests and streams you need to do, it should be properly cached at the appropriate level for your use case.


func createClient(endpoint string, token string) pb.GraphQLClient {

    credential := oauth.NewOauthAccess(&oauth2.Token{AccessToken: token, TokenType: "Bearer"})
    transportCreds := credentials.NewClientTLSFromCert(nil, "")
    conn, err := grpc.Dial(endpoint,
        grpc.WithPerRPCCredentials(credential),
        grpc.WithTransportCredentials(transportCreds),
    )
    panicIfError(err)

    return pb.NewGraphQLClient(conn)
}

def create_client(token, endpoint):
    channel = grpc.secure_channel(endpoint,
        credentials = grpc.composite_channel_credentials(
            grpc.ssl_channel_credentials(),
            grpc.access_token_call_credentials(token)
    ))

    return graphql_pb2_grpc.GraphQLStub(channel)

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?


const operationETH = `subscription {
  searchTransactions(indexName:CALLS, query:"-value:0 type:call", lowBlockNum: -1) {
    undo cursor
    node { hash matchingCalls { from to value(encoding:ETHER) } }
  }
}`

type ethereumDocument struct {
    SearchTransactions struct {
        Cursor string
        Undo   bool
        Node   struct {
            Hash          string
            MatchingCalls []struct {
                From  string
                To    string
                Value string
            }
        }
    }
}

OPERATION_ETH = """subscription {
  searchTransactions(indexName: CALLS, query: "-value:0 type:call", lowBlockNum: -1) {
    undo cursor
    node { hash matchingCalls { from to value(encoding:ETHER) } }
  }
}"""

And we can finally define the code needed to perform your first stream using dfuse Search. The snippet initiate the connection with dfuse servers and start streaming transfers forever.


func streamEthereum(ctx context.Context, client pb.GraphQLClient) {

    executor, err := client.Execute(ctx, &pb.Request{Query: operationETH})
    panicIfError(err)

    for {
        resp, err := executor.Recv()
        panicIfError(err)

        if len(resp.Errors) > 0 {
            for _, err := range resp.Errors {
                fmt.Printf("Request failed: %s\n", err)
            }

            /* We continue here, but you could take another decision here, like exiting the process */
            continue
        }

        document := &ethereumDocument{}
        err = json.Unmarshal([]byte(resp.Data), document)
        panicIfError(err)

        result := document.SearchTransactions
        reverted := ""
        if result.Undo {
            reverted = " REVERTED"
        }

        for _, call := range result.Node.MatchingCalls {
            fmt.Printf("Transfer %s -> %s [%s Ether]%s\n", call.From, call.To, call.Value, reverted)
        }
    }
}

def stream_ethereum(client):
    # The client can be re-used for all requests, cache it at the appropriate level
    stream = client.Execute(Request(query = OPERATION_ETH))

    for rawResult in stream:
        if rawResult.errors:
            print("An error occurred")
            print(rawResult.errors)
        else:
            result = json.loads(rawResult.data)
            for call in result['searchTransactions']['node']['matchingCalls']:
                undo = result['searchTransactions']['undo']
                print("Transfer %s -> %s [%s Ether]%s" % (call['from'], call['to'], call['value'], " REVERTED" if undo else ""))

Run the code


DFUSE_API_KEY="your dfuse api key here" go run main.go

DFUSE_API_KEY="your dfuse api key here" python main.py

What to do next?