gRPC Integration
This guide explains how to integrate with TradeX gRPC services for high-performance internal service communication.
Overview
Section titled “Overview”TradeX uses gRPC for internal service-to-service communication, providing type-safe, high-performance APIs.
Prerequisites
Section titled “Prerequisites”- gRPC client library for your language
- Protocol Buffer compiler (protoc)
- Service protobuf definitions
Service Definitions
Section titled “Service Definitions”Available Services
Section titled “Available Services”- Market Data Service: Market data queries and streaming
- Metadata Service: Instrument configuration queries
- Wallet Service: Balance operations and holds
- Risk Service: Risk calculations
TypeScript/Node.js
Section titled “TypeScript/Node.js”Installation
Section titled “Installation”npm install @grpc/grpc-js @grpc/proto-loaderClient Setup
Section titled “Client Setup”import * as grpc from '@grpc/grpc-js';import * as protoLoader from '@grpc/proto-loader';
const packageDefinition = protoLoader.loadSync( 'path/to/service.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });
const proto = grpc.loadPackageDefinition(packageDefinition);const client = new proto.marketdata.MarketDataService( 'marketdata-service:50051', grpc.credentials.createInsecure());Making Requests
Section titled “Making Requests”// Unary callclient.GetOrderBook( { symbol: 'BTCUSDT-PERP', depth: 20 }, (error, response) => { if (error) { console.error('Error:', error); return; } console.log('Order book:', response); });
// Promise-basedconst orderBook = await new Promise((resolve, reject) => { client.GetOrderBook( { symbol: 'BTCUSDT-PERP', depth: 20 }, (error, response) => { if (error) reject(error); else resolve(response); } );});Server Streaming
Section titled “Server Streaming”const call = client.WatchConfigUpdates({});
call.on('data', (update) => { console.log('Config update:', update);});
call.on('error', (error) => { console.error('Stream error:', error);});
call.on('end', () => { console.log('Stream ended');});Python
Section titled “Python”Installation
Section titled “Installation”pip install grpcio grpcio-toolsGenerate Client Code
Section titled “Generate Client Code”python -m grpc_tools.protoc \ -I./proto \ --python_out=./generated \ --grpc_python_out=./generated \ ./proto/service.protoClient Setup
Section titled “Client Setup”import grpcimport generated.service_pb2 as service_pb2import generated.service_pb2_grpc as service_pb2_grpc
channel = grpc.insecure_channel('marketdata-service:50051')stub = service_pb2_grpc.MarketDataServiceStub(channel)Making Requests
Section titled “Making Requests”# Unary callrequest = service_pb2.GetOrderBookRequest( symbol='BTCUSDT-PERP', depth=20)response = stub.GetOrderBook(request)print('Order book:', response)
# Server streamingrequest = service_pb2.WatchConfigUpdatesRequest()for update in stub.WatchConfigUpdates(request): print('Config update:', update)Installation
Section titled “Installation”go get google.golang.org/grpcgo get google.golang.org/protobufGenerate Client Code
Section titled “Generate Client Code”protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ proto/service.protoClient Setup
Section titled “Client Setup”import ( "google.golang.org/grpc" pb "path/to/generated/proto")
conn, err := grpc.Dial( "marketdata-service:50051", grpc.WithInsecure(),)if err != nil { log.Fatalf("Failed to connect: %v", err)}defer conn.Close()
client := pb.NewMarketDataServiceClient(conn)Making Requests
Section titled “Making Requests”// Unary callctx := context.Background()req := &pb.GetOrderBookRequest{ Symbol: "BTCUSDT-PERP", Depth: 20,}resp, err := client.GetOrderBook(ctx, req)if err != nil { log.Fatalf("Error: %v", err)}fmt.Printf("Order book: %v\n", resp)
// Server streamingstream, err := client.WatchConfigUpdates(ctx, &pb.WatchConfigUpdatesRequest{})if err != nil { log.Fatalf("Error: %v", err)}
for { update, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("Error: %v", err) } fmt.Printf("Config update: %v\n", update)}Error Handling
Section titled “Error Handling”gRPC Status Codes
Section titled “gRPC Status Codes”import { status } from '@grpc/grpc-js';
try { const response = await callRPC();} catch (error) { if (error.code === status.NOT_FOUND) { console.error('Resource not found'); } else if (error.code === status.UNAVAILABLE) { console.error('Service unavailable'); } else { console.error('Error:', error); }}Retry Logic
Section titled “Retry Logic”async function callWithRetry(client, method, request, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await callRPC(client, method, request); } catch (error) { if (error.code === status.UNAVAILABLE && i < maxRetries - 1) { await sleep(1000 * Math.pow(2, i)); continue; } throw error; } }}Authentication
Section titled “Authentication”mTLS (Optional)
Section titled “mTLS (Optional)”const credentials = grpc.credentials.createSsl( fs.readFileSync('ca.crt'), fs.readFileSync('client.key'), fs.readFileSync('client.crt'));
const client = new proto.Service( 'service:50051', credentials);API Keys (If Required)
Section titled “API Keys (If Required)”const metadata = new grpc.Metadata();metadata.add('authorization', `Bearer ${apiKey}`);
client.GetOrderBook( request, metadata, (error, response) => { // handle response });Best Practices
Section titled “Best Practices”Connection Management
Section titled “Connection Management”- Reuse Connections: Create clients once and reuse
- Connection Pooling: Use connection pools for high throughput
- Graceful Shutdown: Close connections on shutdown
Timeouts
Section titled “Timeouts”const deadline = new Date();deadline.setSeconds(deadline.getSeconds() + 5); // 5 second timeout
client.GetOrderBook( request, { deadline }, (error, response) => { // handle response });Monitoring
Section titled “Monitoring”// Track metricsconst metrics = { requests: 0, errors: 0, latency: []};
const startTime = Date.now();client.GetOrderBook(request, (error, response) => { metrics.requests++; if (error) metrics.errors++; metrics.latency.push(Date.now() - startTime);});