gRPC
gRPC provides type-safe, high-performance streaming with built-in filtering capabilities using Protocol Buffers.
grpcurl grpc-datastream.hypetech.games:50051 datastream.DataStream/ListGames
Overview
| Property | Value |
|---|---|
| Protocol | HTTP/2 |
| Direction | Server Streaming |
| Latency | ~2ms |
| Type Safety | Protocol Buffers |
| Built-in Filters | Yes |
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
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
| Step | Component | Action | Latency |
|---|---|---|---|
| 1 | Postgres | INSERT INTO rounds | - |
| 2 | Debezium | Reads WAL, publishes to NATS | ~1-5ms |
| 3 | NATS | Delivers to Consumer | <1ms |
| 4 | Consumer | Processes, publishes to Redis | <1ms |
| 5 | Redis Pub/Sub | Delivers to Backend | <1ms |
| 6 | Backend gRPC | stream.Send(result) | <1ms |
| 7 | Client | Receives protobuf message | - |
Total end-to-end latency: ~2ms
Endpoints
| Method | Description |
|---|---|
StreamByGame | Stream rounds by game slug with filters |
StreamByType | Stream rounds by game type with filters |
StreamAll | Stream all rounds with filters |
GetLatest | Get latest round for a game |
GetHistory | Get historical rounds with filters |
ListGames | List all available games |
Production Endpoint
grpc-datastream.hypetech.games:50051
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:
| Filter | Type | Description | Example |
|---|---|---|---|
min_point | double | Minimum crash point | 5.0 (point >= 5x) |
max_point | double | Maximum crash point | 10.0 (point <= 10x) |
colors | string[] | Filter by colors (double) | ["red", "black"] |
trends | string[] | 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
| Variable | Default | Description |
|---|---|---|
GRPC_URL | grpc-datastream.hypetech.games:50051 | gRPC server address (TLS) |
API_KEY | - | API key for authentication |
GAME | crash | Default game to stream |
MIN_POINT | 0 | Minimum point filter |
Best Practices
Connection Management
- Reuse connections - Create one client, reuse for multiple calls
- Handle cancellation - Use context cancellation for cleanup
- Implement retries - Use exponential backoff for transient failures
- Monitor health - Use gRPC health checking
Streaming
- Handle errors - Check for
io.EOFvs actual errors - Process async - Don't block the receive loop
- Use filters - Reduce network traffic with server-side filtering
- Buffer wisely - Don't accumulate too many unprocessed messages
Security
- Use TLS in production - gRPC over HTTPS
- Authenticate - Use metadata for auth tokens
- Rate limit - Protect against abuse
- Validate input - Check filter values
Comparison with Other Protocols
| Feature | gRPC | WebSocket | SSE |
|---|---|---|---|
| Type safety | Yes (Protobuf) | No (JSON) | No (JSON) |
| Bidirectional | Server streaming | Yes | No |
| Filters | Built-in | Manual | Manual |
| Binary format | Yes | Optional | No |
| Code generation | Automatic | Manual | Manual |
| Browser support | Via proxy | Universal | Universal |
| 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
| Endpoint | gRPC Method | Description |
|---|---|---|
GET /api/grpc/games | ListGames | List all available games |
GET /api/grpc/latest/:game | GetLatest | Get 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 Case | Recommendation |
|---|---|
| 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
| Method | Typical 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