Skip to content

WebSocket Transport

The WebSocket transport provides persistent connections for IRPC, offering lower latency and real-time capabilities compared to HTTP.

Overview

WebSocket transport maintains a single persistent connection that handles multiple IRPC calls without reconnection overhead. This eliminates HTTP handshake latency and enables real-time communication patterns.

Installation

bash
npm install @irpclib/ws

Basic Usage

1. Declare Functions (Shared)

typescript
// rpc/hello/index.ts
import { irpc } from '../lib/module.js';

export type HelloFn = (name: string) => Promise<string>;
export const hello = irpc.declare<HelloFn>({ name: 'hello' });

2. Implement Handlers (Server)

typescript
// rpc/hello/constructor.ts
import { irpc } from '../lib/module.js';
import { hello } from './index.js';

irpc.construct(hello, async (name) => `Hello ${name}`);

3. Server Setup

typescript
// server.ts
import { WebSocketRouter } from '@irpclib/ws';
import { irpc, transport } from './lib/module.js';
import './rpc/hello/constructor.js';

const router = new WebSocketRouter(irpc, transport);

Bun.serve({
  port: 8080,
  fetch(req, server) {
    if (server.upgrade(req)) return;
    return new Response('WebSocket server running');
  },
  websocket: {
    async message(ws, message) {
      await router.resolve(message.toString(), ws);
    },
  },
});

4. Client Usage

typescript
// client.ts
import { hello } from './rpc/hello/index.js';

const message = await hello('John');
console.log(message); // 'Hello John'

Configuration

WebSocketTransportConfig

typescript
type WebSocketTransportConfig = {
  // WebSocket connection
  url: string;                    // WebSocket URL to connect to
  protocols?: string[];          // Sub-protocols for the connection

  // Reconnection
  autoReconnect?: boolean;        // Enable automatic reconnection (default: true)
  maxReconnectAttempts?: number;  // Maximum reconnection attempts (default: 5)
  reconnectDelay?: number;        // Delay between reconnection attempts (default: 1000ms)

  // Timeouts
  connectionTimeout?: number;     // Connection establishment timeout (default: 10000ms)
  timeout?: number;               // Request timeout (inherited from base)

  // Batching
  debounce?: number | boolean;    // Batching delay (inherited from base)

  // Retry
  maxRetries?: number;            // Maximum retry attempts (inherited from base)
  retryMode?: 'linear' | 'exponential'; // Retry strategy (inherited from base)
  retryDelay?: number;            // Base retry delay (inherited from base)

  // Headers (for WebSocket upgrade request)
  headers?: Record<string, string>;
};

Connection Management

Connection States

The transport provides real-time connection state monitoring:

typescript
// Check current state
console.log(transport.state); // 0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED

// Check if connected
if (transport.isOpen) {
  await someFunction();
}

Auto-Reconnection

WebSocket transport automatically handles connection failures:

typescript
const transport = new WebSocketTransport({
  url: 'ws://localhost:8080',
  autoReconnect: true,
  maxReconnectAttempts: 5,
  reconnectDelay: 1000, // 1 second between attempts
});

// Manual reconnection
await transport.reconnect();

Connection Events

Monitor connection lifecycle:

typescript
// The transport handles reconnection internally
// Connection state can be checked via transport.state
// Calls are automatically queued during reconnection

Performance Benefits

Lower Latency

  • No HTTP handshake - Persistent connection eliminates TCP handshake overhead per request
  • Immediate messaging - Messages sent without HTTP request/response cycle
  • Connection reuse - Same WebSocket connection for multiple calls

Automatic Batching

Multiple simultaneous calls are batched into single WebSocket messages:

typescript
// These calls are batched into 1 WebSocket message
const [user, posts, stats] = await Promise.all([
  getUser('123'),
  getPosts('123'),
  getStats('123'),
]);

Streaming Responses

Because the WebSocket channel persists, responses are yielded dynamically as continuous IRPCPacketStream chunks over the socket. This enables you to attach .subscribe() across any standard pipeline to track real-time events.

Error Handling

Network Errors

WebSocket transport includes retry logic for network failures:

typescript
const transport = new WebSocketTransport({
  url: 'ws://localhost:8080',
  maxRetries: 3,
  retryMode: 'exponential', // 1s, 2s, 4s delays
  retryDelay: 1000,
});

Network errors trigger automatic retries. Handler errors fail immediately without retry.

Connection Failures

Connection failures trigger reconnection attempts. Pending calls are queued and sent once reconnected.

Advanced Usage

Custom Headers

Include headers in the WebSocket upgrade request:

typescript
const transport = new WebSocketTransport({
  url: 'ws://localhost:8080',
  headers: {
    'Authorization': 'Bearer token',
    'X-API-Key': 'key',
  },
});

Middleware

Add middleware to the WebSocket router:

typescript
router.use(async () => {
  // Access request context
  const req = getContext<Request>('request');

  // Validate authentication
  const token = req.headers.get('authorization');
  if (!token) {
    throw new Error('Unauthorized');
  }

  // Set context for handlers
  setContext('userId', decodeToken(token).userId);
});

Custom Protocols

Specify WebSocket sub-protocols:

typescript
const transport = new WebSocketTransport({
  url: 'ws://localhost:8080',
  protocols: ['irpc', 'json'],
});

Comparison with HTTP Transport

FeatureWebSocketHTTP
Connection TypePersistentRequest/Response
LatencyLowerHigher
OverheadMinimalHTTP headers
Real-timeYesNo
BatchingAutomaticAutomatic
Browser SupportExcellentUniversal
Setup ComplexityMediumSimple

Choose WebSocket transport when:

  • You need persistent connections
  • Lower latency is critical
  • You're building real-time applications
  • You want bidirectional communication

Choose HTTP transport when:

  • You need maximum compatibility
  • You're building REST-like APIs
  • Simplicity is preferred
  • You have existing HTTP infrastructure

Troubleshooting

Connection Issues

Problem: Connection fails to establish

Solutions:

  • Check WebSocket URL format (ws:// or wss://)
  • Verify server is running and accepting WebSocket connections
  • Check firewall/proxy settings
  • Use browser dev tools to inspect WebSocket handshake

Reconnection Problems

Problem: Auto-reconnection not working

Solutions:

  • Ensure autoReconnect: true is set
  • Check maxReconnectAttempts value
  • Verify server accepts reconnections
  • Monitor transport.state for connection status

Performance Issues

Problem: High latency or slow responses

Solutions:

  • Verify WebSocket connection is persistent
  • Check for unnecessary reconnections
  • Monitor batching efficiency
  • Use appropriate timeout values

Next Steps