Skip to content
Logo

Transports Overview

Transports are the core abstraction in rpckit. They handle the low-level communication with JSON-RPC servers.

Transport Interface

All transports implement the same interface:

interface Transport<S extends Schema = Schema> {
  connect(): Promise<void>
  request<M extends Method>(method: M, ...params: Params<M>): Promise<Return<M>>
  subscribe<M extends Method>(method: M, ...args: [...Params<M>, (data: Return<M>) => void]): Promise<Unsubscribe>
  close(): Promise<void>
}

Methods

MethodDescription
connect()Establish connection to the server
request(method, ...params)Send a JSON-RPC request and wait for response
subscribe(method, ...args)Subscribe to notifications (WebSocket/TCP only)
close()Close the connection and clean up resources

Transport Types

Base Transports

TransportPackageProtocolsSubscriptionsEnvironment
WebSocket@rpckit/websocketws://, wss://YesBrowser, Node.js
TCP@rpckit/tcptcp://, tcp+tls://YesNode.js only
HTTP@rpckit/httphttp://, https://NoBrowser, Node.js

Meta-Transports

Meta-transports wrap other transports to add functionality:

TransportPackagePurpose
Fallback@rpckit/fallbackFailover across multiple transports
Cluster@rpckit/clusterM-of-N quorum consensus

Common Configuration

All transports support these common options:

Timeout

Maximum time to wait for a response:

const transport = webSocket('wss://example.com', {
  timeout: 10000 // 10 seconds (default: 30000)
})

Batching

Combine multiple requests into a single JSON-RPC batch:

const transport = webSocket('wss://example.com', {
  batch: {
    wait: 10,       // Wait 10ms to collect requests (default: 0)
    batchSize: 100  // Max requests per batch (default: 100)
  }
})

Set batch: false to disable batching entirely.

Retry

Automatic retry with exponential backoff (applied to connection, not individual requests):

const transport = webSocket('wss://example.com', {
  reconnect: {
    retryCount: 3,    // Max attempts (default: 3)
    retryDelay: 150   // Base delay in ms (default: 150)
  }
})

Type Safety

Transports can be typed with a schema for compile-time checking:

type MySchema = {
  requests: [
    { method: 'add'; params: [a: number, b: number]; return: number }
  ]
  subscriptions: [
    { method: 'updates'; params: [topic: string]; return: string }
  ]
}
 
const transport = webSocket<MySchema>('wss://example.com')
 
// TypeScript enforces correct usage
await transport.request('add', 1, 2)           // OK, returns number
await transport.request('unknown')             // Type error!
await transport.request('add', 'a', 'b')      // Type error!

Connection Management

Lazy vs Eager Connection

By default, transports connect lazily on first request:

const transport = webSocket('wss://example.com')
// Not connected yet
 
await transport.request('method')
// Now connected

For explicit control, call connect():

const transport = webSocket('wss://example.com')
await transport.connect()  // Connect immediately

Connection Sharing

Transports with the same configuration share connections:

const t1 = webSocket('wss://example.com')
const t2 = webSocket('wss://example.com')
 
// t1 and t2 share the same WebSocket connection

Cleanup

Always close transports when done:

const transport = webSocket('wss://example.com')
try {
  await transport.request('method')
} finally {
  await transport.close()
}

Subscriptions

WebSocket and TCP transports support subscriptions:

const unsubscribe = await transport.subscribe(
  'events.subscribe',
  'channel-1',
  (data) => {
    console.log('Received:', data)
  }
)
 
// The initial response is delivered to the callback
// Subsequent notifications are also delivered to the callback
 
// Later, unsubscribe
await unsubscribe()

Subscriptions are automatically restored after reconnection.

Configure server-side cleanup with onUnsubscribe:

const transport = webSocket('wss://example.com', {
  onUnsubscribe: ({ request, method, params }) => {
    return request(method.replace('subscribe', 'unsubscribe'), ...params)
  }
})

Protocol Variants

Base transports are protocol-agnostic. For protocol-specific defaults, use the subpath variants:

// Base (generic JSON-RPC)
import { webSocket } from '@rpckit/websocket'
 
// Electrum Cash (handshake, keepAlive, unsubscribe pre-configured)
import { webSocket } from '@rpckit/websocket/electrum-cash'

Available variants: @rpckit/websocket/electrum-cash, @rpckit/tcp/electrum-cash, @rpckit/http/electrum-cash, @rpckit/fallback/electrum-cash

Error Handling

Transports throw errors for:

  • Connection failures
  • Request timeouts
  • JSON-RPC errors from the server
try {
  await transport.request('method')
} catch (error) {
  if (error.code === -32600) {
    // Invalid request
  } else if (error.code === -32601) {
    // Method not found
  }
}