FortiBlox LogoFortiBlox Docs
NexusgRPC Streaming

Getting Started with gRPC Streaming

Install gRPC client libraries and connect to FortiBlox streaming

Getting Started with gRPC Streaming

Connect to FortiBlox gRPC streaming in 4 languages: JavaScript, Python, Rust, and Go.

Prerequisites

  • FortiBlox API key with Professional or Enterprise tier
  • Basic knowledge of gRPC
  • Development environment for your chosen language

Installation

npm install @grpc/grpc-js @grpc/proto-loader

Download the proto file:

curl -O https://raw.githubusercontent.com/fortiblox/geyser-grpc/main/protos/geyser.proto
pip install grpcio grpcio-tools

Download and compile the proto:

curl -O https://raw.githubusercontent.com/fortiblox/geyser-grpc/main/protos/geyser.proto
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. geyser.proto

Add to Cargo.toml:

[dependencies]
tonic = "0.10"
prost = "0.12"
tokio = { version = "1.0", features = ["full"] }
go get google.golang.org/grpc
go get google.golang.org/protobuf

Connection Setup

Endpoints

NetworkEndpointProtocol
Mainnetgrpc.fortiblox.com:10002HTTP/2 Plaintext (Professional+)

Important: The endpoint uses HTTP/2 plaintext (insecure), not HTTPS/TLS. Configure your client with createInsecure() or equivalent.

Authentication

Add your API key to gRPC metadata:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

// Load proto
const packageDefinition = protoLoader.loadSync('geyser.proto');
const geyserProto = grpc.loadPackageDefinition(packageDefinition).geyser;

// Create client (plaintext/insecure)
const client = new geyserProto.Geyser(
  'grpc.fortiblox.com:10002',
  grpc.credentials.createInsecure()
);

// Add API key to metadata
const metadata = new grpc.Metadata();
metadata.add('x-api-key', process.env.FORTIBLOX_API_KEY);

// Make request with metadata
const stream = client.SubscribeTransactions(request, metadata);
import grpc
import geyser_pb2
import geyser_pb2_grpc
import os

# Create insecure channel (plaintext)
channel = grpc.insecure_channel('grpc.fortiblox.com:10002')

# Create stub
stub = geyser_pb2_grpc.GeyserStub(channel)

# Add API key to metadata
metadata = [('x-api-key', os.getenv('FORTIBLOX_API_KEY'))]

# Make request with metadata
for tx in stub.SubscribeTransactions(request, metadata=metadata):
    print(f"Transaction: {tx.signature}")
use tonic::{Request, metadata::MetadataValue};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect with insecure channel (plaintext)
    let mut client = GeyserClient::connect("http://grpc.fortiblox.com:10002").await?;

    let api_key = std::env::var("FORTIBLOX_API_KEY")?;
    let mut request = Request::new(SubscribeTransactionsRequest::default());
    request.metadata_mut().insert(
        "x-api-key",
        MetadataValue::from_str(&api_key)?
    );

    let stream = client.subscribe_transactions(request).await?.into_inner();
    Ok(())
}
package main

import (
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/metadata"
)

func main() {
    // Use insecure connection (plaintext)
    conn, _ := grpc.Dial(
        "grpc.fortiblox.com:10002",
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    defer conn.Close()

    client := NewGeyserClient(conn)

    // Add API key to context
    ctx := metadata.AppendToOutgoingContext(
        context.Background(),
        "x-api-key", os.Getenv("FORTIBLOX_API_KEY"),
    )

    stream, _ := client.SubscribeTransactions(ctx, &SubscribeTransactionsRequest{})
}

First Stream Subscription

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

// Load and create client (from above)
const packageDefinition = protoLoader.loadSync('geyser.proto');
const geyserProto = grpc.loadPackageDefinition(packageDefinition).geyser;
const client = new geyserProto.Geyser(
  'grpc.fortiblox.com:10002',
  grpc.credentials.createInsecure()
);

// Prepare metadata
const metadata = new grpc.Metadata();
metadata.add('x-api-key', process.env.FORTIBLOX_API_KEY);

// Subscribe to transactions
const stream = client.SubscribeTransactions({
  filter_votes: true,
  commitment: 'FINALIZED'
}, metadata);

stream.on('data', (tx) => {
  console.log(`Transaction: ${tx.signature}`);
  console.log(`  Slot: ${tx.slot}`);
  console.log(`  Success: ${tx.success}`);
});

stream.on('error', (err) => {
  console.error('Stream error:', err);
});

stream.on('end', () => {
  console.log('Stream ended');
});
import grpc
import geyser_pb2
import geyser_pb2_grpc
import os

# Create connection (from above)
channel = grpc.insecure_channel('grpc.fortiblox.com:10002')
stub = geyser_pb2_grpc.GeyserStub(channel)

# Prepare request
request = geyser_pb2.SubscribeTransactionsRequest(
    filter_votes=True,
    commitment=geyser_pb2.FINALIZED
)

# Subscribe with metadata
metadata = [('x-api-key', os.getenv('FORTIBLOX_API_KEY'))]

try:
    for tx in stub.SubscribeTransactions(request, metadata=metadata):
        print(f"Transaction: {tx.signature}")
        print(f"  Slot: {tx.slot}")
        print(f"  Success: {tx.success}")
except grpc.RpcError as e:
    print(f"Error: {e.code()} - {e.details()}")
use tonic::{Request, metadata::MetadataValue};
use futures::stream::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect
    let mut client = GeyserClient::connect(
        "http://grpc.fortiblox.com:10002"
    ).await?;

    // Prepare request
    let mut request = Request::new(SubscribeTransactionsRequest {
        filter_votes: true,
        commitment: Commitment::Finalized as i32,
        ..Default::default()
    });

    // Add API key
    let api_key = std::env::var("FORTIBLOX_API_KEY")?;
    request.metadata_mut().insert(
        "x-api-key",
        MetadataValue::from_str(&api_key)?
    );

    // Subscribe
    let mut stream = client
        .subscribe_transactions(request)
        .await?
        .into_inner();

    // Process messages
    while let Some(tx) = stream.next().await {
        let tx = tx?;
        println!("Transaction: {}", tx.signature);
        println!("  Slot: {}", tx.slot);
        println!("  Success: {}", tx.success);
    }

    Ok(())
}
package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "os"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/metadata"
)

func main() {
    // Connect (plaintext)
    conn, err := grpc.Dial(
        "grpc.fortiblox.com:10002",
        grpc.WithTransportCredentials(insecure.NewCredentials()),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    client := NewGeyserClient(conn)

    // Add API key
    ctx := metadata.AppendToOutgoingContext(
        context.Background(),
        "x-api-key", os.Getenv("FORTIBLOX_API_KEY"),
    )

    // Subscribe
    stream, err := client.SubscribeTransactions(ctx, &SubscribeTransactionsRequest{
        FilterVotes: true,
        Commitment:  Commitment_FINALIZED,
    })
    if err != nil {
        log.Fatal(err)
    }

    // Process messages
    for {
        tx, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }

        fmt.Printf("Transaction: %s\n", tx.Signature)
        fmt.Printf("  Slot: %d\n", tx.Slot)
        fmt.Printf("  Success: %v\n", tx.Success)
    }
}

Health Check

Test your connection with the Ping method:

client.Ping({ count: 1 }, metadata, (err, response) => {
  if (err) {
    console.error('Connection failed:', err);
  } else {
    console.log('Connected!');
    console.log(`Version: ${response.version}`);
    console.log(`Latest Slot: ${response.latest_slot}`);
  }
});

Next Steps