Pular para o conteúdo principal

gRPC

gRPC provides type-safe, high-performance streaming with built-in filtering capabilities using Protocol Buffers.

Try with grpcurl
grpcurl grpc-datastream.hypetech.games:50051 datastream.DataStream/ListGames

Overview

PropertyValue
ProtocolHTTP/2
DirectionServer Streaming
Latency~2ms
Type SafetyProtocol Buffers
Built-in FiltersYes

Authentication

gRPC uses API key via metadata for authentication:

# Using grpcurl with API key (TLS enabled)
grpcurl \
-H "x-api-key: sk_live_xxxxx" \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/ListGames

# Or using authorization header
grpcurl \
-H "authorization: Bearer sk_live_xxxxx" \
-d '{"game_slug":"crash"}' \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/StreamByGame
dica

See the Authentication Guide for detailed information on creating and managing API keys.

Key Features

  • Type-safe: Strongly typed messages via Protocol Buffers
  • Server streaming: Efficient streaming with backpressure
  • Built-in filters: Filter by min_point, max_point, colors, trends
  • Reflection: Discover services with grpcurl
  • Cross-language: Auto-generated clients for any language

Data Flow

StepComponentActionLatency
1PostgresINSERT INTO rounds-
2DebeziumReads WAL, publishes to NATS~1-5ms
3NATSDelivers to Consumer<1ms
4ConsumerProcesses, publishes to Redis<1ms
5Redis Pub/SubDelivers to Backend<1ms
6Backend gRPCstream.Send(result)<1ms
7ClientReceives protobuf message-

Total end-to-end latency: ~2ms

Endpoints

MethodDescription
StreamByGameStream rounds by game slug with filters
StreamByTypeStream rounds by game type with filters
StreamAllStream all rounds with filters
GetLatestGet latest round for a game
GetHistoryGet historical rounds with filters
ListGamesList all available games

Production Endpoint

grpc-datastream.hypetech.games:50051
TLS Required

The gRPC endpoint uses TLS encryption. Do not use -plaintext flag with grpcurl.

Filter Options

The gRPC API supports powerful filtering not available in other protocols:

FilterTypeDescriptionExample
min_pointdoubleMinimum crash point5.0 (point >= 5x)
max_pointdoubleMaximum crash point10.0 (point <= 10x)
colorsstring[]Filter by colors (double)["red", "black"]
trendsstring[]Filter by trends (wall-street)["up", "down"]

Proto Definition

syntax = "proto3";
package datastream;

service DataStream {
rpc StreamByGame(StreamRequest) returns (stream RoundResult);
rpc StreamByType(StreamByTypeRequest) returns (stream RoundResult);
rpc StreamAll(StreamAllRequest) returns (stream RoundResult);
rpc GetLatest(GetLatestRequest) returns (RoundResult);
rpc GetHistory(GetHistoryRequest) returns (HistoryResponse);
rpc ListGames(ListGamesRequest) returns (ListGamesResponse);
}

message Filter {
optional double min_point = 1;
optional double max_point = 2;
repeated string colors = 3;
repeated string trends = 4;
}

message StreamRequest {
string game_slug = 1;
optional Filter filter = 2;
}

message RoundResult {
int64 round_id = 2;
string game_slug = 3;
string game_type = 4;
string finished_at = 6;
string extras = 7; // Raw JSON extras
int64 timestamp = 8; // Unix ms for latency calculation

// Convenience fields (parsed from extras)
optional double point = 9; // For crash games
optional string color = 10; // For double games
optional int32 number = 11; // For double games
optional string trending = 12; // For wall-street games

// Unified value (like REST API response)
string value = 13; // Formatted: "15.90" for crash, "red" for double
}

Client Implementation

Go Client

package main

import (
"context"
"crypto/tls"
"flag"
"fmt"
"log"
"time"

pb "grpc-sample/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
)

const apiKey = "sk_live_xxxxx"

func main() {
gameSlug := flag.String("game", "crash", "Game slug to stream")
minPoint := flag.Float64("min-point", 0, "Minimum point filter")
flag.Parse()

// Create TLS credentials
creds := credentials.NewTLS(&tls.Config{})

conn, err := grpc.NewClient(
"grpc-datastream.hypetech.games:50051",
grpc.WithTransportCredentials(creds),
)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()

client := pb.NewDataStreamClient(conn)

// Create context with API key
ctx := metadata.AppendToOutgoingContext(context.Background(),
"x-api-key", apiKey)

// List games
games, _ := client.ListGames(ctx, &pb.ListGamesRequest{})
for _, game := range games.Games {
fmt.Printf(" - %s (%s)\n", game.Slug, game.Type)
}

// Create filter
var filter *pb.Filter
if *minPoint > 0 {
filter = &pb.Filter{MinPoint: minPoint}
}

// Stream rounds
stream, err := client.StreamByGame(ctx, &pb.StreamRequest{
GameSlug: *gameSlug,
Filter: filter,
})
if err != nil {
log.Fatalf("Failed to stream: %v", err)
}

for {
result, err := stream.Recv()
if err != nil {
log.Fatalf("Stream error: %v", err)
}

latency := time.Now().UnixMilli() - result.Timestamp
// Use unified value field (like REST API)
fmt.Printf("[%s] %s: %s (latency: %dms)\n",
result.GameSlug, result.GameType, result.Value, latency)
}
}

Python Client

import grpc
import time
import argparse
import datastream_pb2
import datastream_pb2_grpc

API_KEY = "sk_live_xxxxx"

def main():
parser = argparse.ArgumentParser()
parser.add_argument("--game", default="crash")
parser.add_argument("--min-point", type=float, default=0)
args = parser.parse_args()

# Create secure channel with TLS
credentials = grpc.ssl_channel_credentials()
channel = grpc.secure_channel("grpc-datastream.hypetech.games:50051", credentials)
stub = datastream_pb2_grpc.DataStreamStub(channel)

# Metadata with API key
metadata = [('x-api-key', API_KEY)]

# List games
games = stub.ListGames(datastream_pb2.ListGamesRequest(), metadata=metadata)
for game in games.games:
print(f" - {game.slug} ({game.type})")

# Create filter
filter_obj = None
if args.min_point > 0:
filter_obj = datastream_pb2.Filter(min_point=args.min_point)

# Stream rounds
request = datastream_pb2.StreamRequest(
game_slug=args.game,
filter=filter_obj
)

for result in stub.StreamByGame(request, metadata=metadata):
latency = int(time.time() * 1000) - result.timestamp
# Use unified value field (like REST API)
print(f"[{result.game_slug}] {result.game_type}: "
f"{result.value} (latency: {latency}ms)")

if __name__ == "__main__":
main()

Node.js Client

import grpc from "@grpc/grpc-js";
import protoLoader from "@grpc/proto-loader";
import path from "path";

const GRPC_URL = "grpc-datastream.hypetech.games:50051";
const API_KEY = "sk_live_xxxxx";
const GAME_SLUG = "crash";
const MIN_POINT = 5.0;

async function main() {
const PROTO_PATH = path.join(__dirname, "datastream.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const datastream = protoDescriptor.datastream;

// Create client with TLS credentials
const client = new datastream.DataStream(
GRPC_URL,
grpc.credentials.createSsl()
);

// Metadata with API key
const metadata = new grpc.Metadata();
metadata.add("x-api-key", API_KEY);

// List games
client.ListGames({}, metadata, (err, response) => {
response.games.forEach((game) => {
console.log(` - ${game.slug} (${game.type})`);
});
});

// Stream with filter
const stream = client.StreamByGame({
game_slug: GAME_SLUG,
filter: { min_point: MIN_POINT },
}, metadata);

stream.on("data", (result) => {
const latency = Date.now() - parseInt(result.timestamp);
// Use unified value field (like REST API)
console.log(
`[${result.game_slug}] ${result.game_type}: ` +
`${result.value} (latency: ${latency}ms)`
);
});
}

main();

Using grpcurl

grpcurl is a command-line tool for interacting with gRPC servers.

Install

# macOS
brew install grpcurl

# Linux
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

Commands

# Set API key variable
API_KEY="sk_live_xxxxx"

# List services (reflection - no auth required)
grpcurl grpc-datastream.hypetech.games:50051 list

# List methods (reflection - no auth required)
grpcurl grpc-datastream.hypetech.games:50051 list datastream.DataStream

# List games (with API key)
grpcurl -H "x-api-key: $API_KEY" \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/ListGames

# Get latest round
grpcurl -H "x-api-key: $API_KEY" \
-d '{"game_slug":"crash"}' \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/GetLatest

# Stream crash games with point >= 5
grpcurl -H "x-api-key: $API_KEY" \
-d '{"game_slug":"crash","filter":{"min_point":5}}' \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/StreamByGame

# Stream double games, only red color
grpcurl -H "x-api-key: $API_KEY" \
-d '{"game_slug":"double","filter":{"colors":["red"]}}' \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/StreamByGame

# Get history with filter
grpcurl -H "x-api-key: $API_KEY" \
-d '{"game_slug":"crash","limit":10,"filter":{"min_point":2,"max_point":10}}' \
grpc-datastream.hypetech.games:50051 \
datastream.DataStream/GetHistory

Environment Variables

VariableDefaultDescription
GRPC_URLgrpc-datastream.hypetech.games:50051gRPC server address (TLS)
API_KEY-API key for authentication
GAMEcrashDefault game to stream
MIN_POINT0Minimum point filter

Best Practices

Connection Management

  1. Reuse connections - Create one client, reuse for multiple calls
  2. Handle cancellation - Use context cancellation for cleanup
  3. Implement retries - Use exponential backoff for transient failures
  4. Monitor health - Use gRPC health checking

Streaming

  1. Handle errors - Check for io.EOF vs actual errors
  2. Process async - Don't block the receive loop
  3. Use filters - Reduce network traffic with server-side filtering
  4. Buffer wisely - Don't accumulate too many unprocessed messages

Security

  1. Use TLS in production - gRPC over HTTPS
  2. Authenticate - Use metadata for auth tokens
  3. Rate limit - Protect against abuse
  4. Validate input - Check filter values

Comparison with Other Protocols

FeaturegRPCWebSocketSSE
Type safetyYes (Protobuf)No (JSON)No (JSON)
BidirectionalServer streamingYesNo
FiltersBuilt-inManualManual
Binary formatYesOptionalNo
Code generationAutomaticManualManual
Browser supportVia proxyUniversalUniversal
Latency~2ms~5ms~50ms

When to Use gRPC

Use gRPC when:

  • Type safety is important
  • Server-side filtering is needed
  • Building backend services
  • Need efficient binary protocol
  • Want auto-generated clients
  • Latency < 5ms is critical

Consider alternatives when:

  • Browser clients without proxy (use WebSocket/SSE)
  • Simple data without complex filtering (use SSE)
  • Need bidirectional communication (use WebSocket)
  • Proxy/firewall restrictions (use SSE)

Browser/REST Proxy

For browsers and environments that don't support native gRPC (HTTP/2), we provide a REST proxy that forwards requests to the gRPC server.

Proxy Endpoints

EndpointgRPC MethodDescription
GET /api/grpc/gamesListGamesList all available games
GET /api/grpc/latest/:gameGetLatestGet latest round for a game

Authentication

The proxy uses the same authentication as other REST endpoints:

# Using X-API-Key header
curl -H "X-API-Key: sk_live_xxxxx" \
https://datastream.hypetech.games/api/grpc/games

# Get latest via gRPC proxy
curl -H "X-API-Key: sk_live_xxxxx" \
https://datastream.hypetech.games/api/grpc/latest/crash

Response Format

The proxy returns the same JSON format as native gRPC responses:

{
"round_id": 12345,
"game_slug": "crash",
"game_type": "crash",
"finished_at": "2026-01-21T04:30:00Z",
"value": "2.45",
"point": 2.45,
"timestamp": 1737433800000
}

When to Use the Proxy

Use CaseRecommendation
Browser applications✅ Use proxy (browsers don't support gRPC natively)
Backend services❌ Use native gRPC for better performance
Testing/debugging✅ Use proxy for quick tests with curl
Mobile apps⚠️ Consider native gRPC if available

Latency Comparison

MethodTypical Latency
Native gRPC~2ms
REST Proxy~50-150ms

The proxy adds overhead due to HTTP/1.1 → gRPC translation. For latency-critical applications, use native gRPC clients.


Troubleshooting

Connection Refused

rpc error: code = Unavailable desc = connection error

Solutions:

  • Verify server is running on port 50051
  • Check firewall allows gRPC port
  • Verify DNS resolution

Stream Terminated

rpc error: code = Canceled desc = context canceled

Solutions:

  • Check for timeout in context
  • Verify server didn't crash
  • Check network stability

Invalid Filter

rpc error: code = InvalidArgument desc = invalid filter

Solutions:

  • Verify filter field types
  • Check game supports the filter type
  • Use appropriate filter for game type