Connection

Establish a secure gRPC connection to NextBlock's API using Node.js and @grpc/grpc-js.

Prerequisites

Install the required dependencies:

npm install @grpc/grpc-js @grpc/proto-loader
npm install @solana/web3.js
npm install typescript @types/node  # For TypeScript

Generate the gRPC client from proto specs:

# Clone the proto repository
git clone https://github.com/nextblock-ag/nextblock-proto
# Follow the JavaScript/TypeScript generation instructions in the repo

Connection Setup

import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { promisify } from 'util';

// Configuration interface
interface NextBlockConfig {
  endpoint: string;
  apiKey: string;
  useTLS: boolean;
  timeout: number;
  keepaliveTimeMs: number;
  keepaliveTimeoutMs: number;
}

// Default configuration
const defaultConfig: NextBlockConfig = {
  endpoint: 'fra.nextblock.io:443',
  apiKey: process.env.NEXTBLOCK_API_KEY || '',
  useTLS: true,
  timeout: 30000,
  keepaliveTimeMs: 60000,  // 60 seconds
  keepaliveTimeoutMs: 15000, // 15 seconds
};

// Authentication metadata interceptor
class AuthInterceptor {
  private apiKey: string;

  constructor(apiKey: string) {
    this.apiKey = apiKey;
  }

  // Add authorization header to all requests
  intercept(options: any, nextCall: any) {
    return new grpc.InterceptingCall(nextCall(options), {
      start: (metadata: grpc.Metadata, listener: any, next: any) => {
        metadata.set('authorization', this.apiKey);
        next(metadata, listener);
      },
    });
  }
}

// Create gRPC channel with authentication
export async function createNextBlockChannel(config: NextBlockConfig): Promise<grpc.Channel> {
  // Channel options for keepalive and performance
  const channelOptions: grpc.ChannelOptions = {
    'grpc.keepalive_time_ms': config.keepaliveTimeMs,
    'grpc.keepalive_timeout_ms': config.keepaliveTimeoutMs,
    'grpc.keepalive_permit_without_stream': 1,
    'grpc.http2.max_pings_without_data': 0,
    'grpc.http2.min_ping_interval_without_data_ms': 300000, // 5 minutes
  };

  // Create credentials
  const credentials = config.useTLS 
    ? grpc.credentials.createSsl()
    : grpc.credentials.createInsecure();

  // Create channel
  const channel = new grpc.Channel(config.endpoint, credentials, channelOptions);
  
  return channel;
}

// Create authenticated gRPC client
export async function createNextBlockClient(config: NextBlockConfig = defaultConfig) {
  // Validate configuration
  if (!config.apiKey) {
    throw new Error('API key is required');
  }

  // Create channel
  const channel = await createNextBlockChannel(config);

  // Create authentication interceptor
  const authInterceptor = new AuthInterceptor(config.apiKey);

  // Load proto definition (replace with your generated client)
  /* Example proto loading - replace with your actual proto
  const packageDefinition = protoLoader.loadSync('path/to/your/nextblock.proto', {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  });

  const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
  const ApiClient = protoDescriptor.nextblock.Api;

  // Create client with interceptor
  const client = new ApiClient(config.endpoint, credentials, {
    interceptors: [authInterceptor.intercept.bind(authInterceptor)],
    ...channelOptions,
  });
  */

  console.log(`Connected to NextBlock at ${config.endpoint}`);
  
  return {
    channel,
    // client, // Uncomment when you have the generated client
    config,
  };
}

// Connection manager with health checking
export class NextBlockConnectionManager {
  private config: NextBlockConfig;
  private channel?: grpc.Channel;
  private client?: any; // Replace with your generated client type
  private isConnected: boolean = false;

  constructor(config: NextBlockConfig = defaultConfig) {
    this.config = config;
  }

  // Establish connection
  async connect(): Promise<boolean> {
    try {
      const connection = await createNextBlockClient(this.config);
      this.channel = connection.channel;
      // this.client = connection.client;

      // Test the connection
      await this.healthCheck();
      this.isConnected = true;
      console.log(`Successfully connected to NextBlock at ${this.config.endpoint}`);
      return true;

    } catch (error) {
      console.error('Failed to connect to NextBlock:', error);
      this.isConnected = false;
      return false;
    }
  }

  // Health check
  async healthCheck(): Promise<boolean> {
    if (!this.channel) {
      return false;
    }

    return new Promise((resolve) => {
      // Check channel state
      const state = this.channel!.getConnectivityState(false);
      
      if (state === grpc.connectivityState.READY) {
        console.log('Connection health check passed');
        resolve(true);
      } else {
        console.log(`Connection state: ${grpc.connectivityState[state]}`);
        resolve(false);
      }
    });
  }

  // Disconnect
  async disconnect(): Promise<void> {
    if (this.channel) {
      this.channel.close();
      this.isConnected = false;
      console.log('Disconnected from NextBlock');
    }
  }

  // Getters
  get connected(): boolean {
    return this.isConnected;
  }

  get grpcClient(): any {
    return this.client;
  }
}

// Configuration from environment variables
export function configFromEnv(): NextBlockConfig {
  return {
    endpoint: process.env.NEXTBLOCK_ENDPOINT || 'fra.nextblock.io:443',
    apiKey: process.env.NEXTBLOCK_API_KEY || '',
    useTLS: process.env.NEXTBLOCK_USE_TLS !== 'false',
    timeout: parseInt(process.env.NEXTBLOCK_TIMEOUT || '30000'),
    keepaliveTimeMs: parseInt(process.env.NEXTBLOCK_KEEPALIVE_TIME_MS || '60000'),
    keepaliveTimeoutMs: parseInt(process.env.NEXTBLOCK_KEEPALIVE_TIMEOUT_MS || '15000'),
  };
}

Usage Examples

Available Endpoints

  • Frankfurt: frankfurt.nextblock.io:443 (Europe)

  • Amsterdam: amsterdam.nextblock.io:443 (Europe)

  • London: london.nextblock.io:443 (Europe)

  • Singapore: singapore.nextblock.io:443 (Asia)

  • Tokyo: tokyo.nextblock.io:443 (Asia)

  • New York: ny.nextblock.io:443 (US East)

  • Salt Lake City: slc.nextblock.io:443 (US West)

  • Dublin: dublin.nextblock.io:443 (Europe)

  • Vilnius: vilnius.nextblock.io:443 (Europe)

Best Practices

  1. Use TypeScript: Leverage type safety for better development experience

  2. Enable TLS: Always use secure connections in production

  3. Implement keepalive: Configure appropriate keepalive settings for persistent connections

  4. Handle errors gracefully: Use retry logic with exponential backoff

  5. Validate configuration: Check all required settings before connecting

  6. Use connection pooling: For high-throughput applications

  7. Monitor connection health: Implement regular health checks

  8. Environment variables: Store sensitive configuration securely

  9. Close connections properly: Always close connections when done

  10. Use interceptors: Implement authentication and logging via gRPC interceptors

Last updated