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-loaderDownload the proto file:
curl -O https://raw.githubusercontent.com/fortiblox/geyser-grpc/main/protos/geyser.protopip install grpcio grpcio-toolsDownload 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.protoAdd 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/protobufConnection Setup
Endpoints
| Network | Endpoint | Protocol |
|---|---|---|
| Mainnet | grpc.fortiblox.com:10002 | HTTP/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
- Transaction Streaming - Advanced filtering
- Account Streaming - Monitor accounts
- Historical Replay - Replay historical data