# Connection

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

This page shows the connection pattern and authentication interceptor. Replace the placeholder generated client wiring with the client generated from [`nextblock-proto`](https://github.com/nextblock-ag/nextblock-proto).

## Prerequisites

Install the required dependencies:

```bash
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:

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

## Connection Setup

<pre class="language-typescript"><code class="lang-typescript"><strong>import * as grpc from '@grpc/grpc-js';
</strong>import * as protoLoader from '@grpc/proto-loader';
import { promisify } from 'util';

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

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

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

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

<strong>  // Add authorization header to all requests
</strong>  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);
      },
    });
  }
}

<strong>// Create gRPC channel with authentication
</strong>export async function createNextBlockChannel(config: NextBlockConfig): Promise&#x3C;grpc.Channel> {
<strong>  // Channel options for keepalive and performance
</strong>  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
  };

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

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

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

<strong>  // Create channel
</strong>  const channel = await createNextBlockChannel(config);

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

<strong>  // Load proto definition (replace with your generated client)
</strong>  /* 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,
  };
}

<strong>// Connection manager with health checking
</strong>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;
  }

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

<strong>      // Test the connection
</strong>      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;
    }
  }

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

    return new Promise((resolve) => {
<strong>      // Check channel state
</strong>      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);
      }
    });
  }

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

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

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

<strong>// Configuration from environment variables
</strong>export function configFromEnv(): NextBlockConfig {
  return {
    endpoint: process.env.NEXTBLOCK_ENDPOINT || 'frankfurt.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'),
  };
}
</code></pre>

## Usage Examples

<pre class="language-typescript"><code class="lang-typescript"><strong>// Basic connection example
</strong>async function basicConnectionExample() {
  const config: NextBlockConfig = {
    endpoint: 'frankfurt.nextblock.io:443',
    apiKey: '&#x3C;your-api-key-here>',
    useTLS: true,
    timeout: 30000,
    keepaliveTimeMs: 60000,
    keepaliveTimeoutMs: 15000,
  };

  try {
    const connection = await createNextBlockClient(config);
    console.log('Connected to NextBlock!');

<strong>    // Your API calls would go here
</strong>    // const response = await connection.client.ping({});
    
<strong>    // Clean up
</strong>    connection.channel.close();
    
  } catch (error) {
    console.error('Connection failed:', error);
  }
}

<strong>// Connection manager example (recommended)
</strong>async function connectionManagerExample() {
  const config = configFromEnv();
  const manager = new NextBlockConnectionManager(config);

  try {
    const connected = await manager.connect();
    
    if (connected) {
      console.log('Connection manager established connection');
      
<strong>      // Use manager.grpcClient for API calls
</strong>      // const response = await manager.grpcClient.ping({});
      
<strong>      // Keep connection alive for your application
</strong>      await new Promise(resolve => setTimeout(resolve, 5000));
      
    } else {
      console.error('Failed to establish connection');
    }
    
  } finally {
    await manager.disconnect();
  }
}

<strong>// Advanced connection with retry logic
</strong>class EnhancedConnectionManager extends NextBlockConnectionManager {
  private maxRetries: number = 3;
  private retryDelay: number = 5000; // 5 seconds

  async connectWithRetry(): Promise&#x3C;boolean> {
    for (let attempt = 1; attempt &#x3C;= this.maxRetries; attempt++) {
      console.log(`Connection attempt ${attempt}/${this.maxRetries}`);
      
      const success = await this.connect();
      if (success) {
        return true;
      }

      if (attempt &#x3C; this.maxRetries) {
        console.log(`Retrying in ${this.retryDelay}ms...`);
        await new Promise(resolve => setTimeout(resolve, this.retryDelay));
        this.retryDelay *= 2; // Exponential backoff
      }
    }

    console.error('All connection attempts failed');
    return false;
  }

  async callWithRetry&#x3C;T>(
    fn: () => Promise&#x3C;T>,
    maxRetries: number = 3
  ): Promise&#x3C;T> {
    let lastError: Error;

    for (let attempt = 1; attempt &#x3C;= maxRetries; attempt++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error as Error;
        
<strong>        // Don't retry on authentication errors
</strong>        if (error &#x26;&#x26; (error as any).code === grpc.status.UNAUTHENTICATED) {
          throw error;
        }

        if (attempt &#x3C; maxRetries) {
          const delay = Math.min(1000 * Math.pow(2, attempt - 1), 30000);
          console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }

    throw lastError!;
  }
}

<strong>// Connection pool for high-throughput applications
</strong>class ConnectionPool {
  private connections: NextBlockConnectionManager[] = [];
  private currentIndex: number = 0;
  private config: NextBlockConfig;
  private poolSize: number;

  constructor(config: NextBlockConfig, poolSize: number = 5) {
    this.config = config;
    this.poolSize = poolSize;
  }

  async initialize(): Promise&#x3C;void> {
    console.log(`Initializing connection pool with ${this.poolSize} connections...`);
    
    const connectionPromises = Array.from({ length: this.poolSize }, async (_, i) => {
      const manager = new NextBlockConnectionManager(this.config);
      const connected = await manager.connect();
      
      if (connected) {
        this.connections.push(manager);
        console.log(`Connection ${i + 1} established`);
      } else {
        console.warn(`Failed to create connection ${i + 1}`);
      }
    });

    await Promise.all(connectionPromises);
    console.log(`Connection pool initialized with ${this.connections.length} connections`);
  }

  getConnection(): NextBlockConnectionManager {
    if (this.connections.length === 0) {
      throw new Error('No available connections in pool');
    }

    const connection = this.connections[this.currentIndex];
    this.currentIndex = (this.currentIndex + 1) % this.connections.length;
    return connection;
  }

  async closeAll(): Promise&#x3C;void> {
    await Promise.all(this.connections.map(conn => conn.disconnect()));
    this.connections = [];
    console.log('All connections closed');
  }
}

<strong>// Run examples
</strong>async function main() {
  console.log('NextBlock Connection Examples');
  
<strong>  // Example 1: Basic connection
</strong>  console.log('\n1. Basic Connection:');
  await basicConnectionExample();
  
<strong>  // Example 2: Connection manager
</strong>  console.log('\n2. Connection Manager:');
  await connectionManagerExample();
  
<strong>  // Example 3: Enhanced connection with retry
</strong>  console.log('\n3. Enhanced Connection:');
  const enhancedManager = new EnhancedConnectionManager(configFromEnv());
  const connected = await enhancedManager.connectWithRetry();
  if (connected) {
    console.log('Enhanced connection successful');
    await enhancedManager.disconnect();
  }
}

<strong>// Export for use in other modules
</strong>export {
  NextBlockConfig,
  NextBlockConnectionManager,
  EnhancedConnectionManager,
  ConnectionPool,
  configFromEnv,
};

if (require.main === module) {
  main().catch(console.error);
}
</code></pre>

## 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nextblock.io/api/examples/javascript/connection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
