or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client.mdcommon.mdindex.mdserver-adapters.mdserver.md
tile.json

client.mddocs/

Client API

GraphQL over WebSocket client implementation providing real-time GraphQL subscriptions with comprehensive connection management, event handling, and retry capabilities.

Capabilities

Client Creation

Creates a disposable GraphQL over WebSocket client with extensive configuration options.

/**
 * Creates a disposable GraphQL over WebSocket client
 * @param options - Client configuration options
 * @returns Client instance with subscription and event capabilities
 */
function createClient<P = Record<string, unknown>>(
  options: ClientOptions<P>
): Client;

interface ClientOptions<P = Record<string, unknown>> {
  /** WebSocket URL or function returning URL */
  url: string | (() => Promise<string> | string);
  /** Connection parameters or function returning them */
  connectionParams?: P | (() => Promise<P> | P);
  /** Whether to establish connection lazily (default: true) */
  lazy?: boolean;
  /** Error handler for non-lazy connections */
  onNonLazyError?: (errorOrCloseEvent: unknown) => void;
  /** Timeout for lazy connection closure in milliseconds */
  lazyCloseTimeout?: number;
  /** Keep-alive ping interval in milliseconds */
  keepAlive?: number;
  /** Connection acknowledgment wait timeout in milliseconds */
  connectionAckWaitTimeout?: number;
  /** Disable automatic pong responses */
  disablePong?: boolean;
  /** Number of retry attempts for failed connections */
  retryAttempts?: number;
  /** Retry delay strategy function */
  retryWait?: (retries: number) => Promise<void>;
  /** Function to determine if retry should be attempted */
  shouldRetry?: (errOrCloseEvent: unknown) => boolean;
  /** Event listeners for connection lifecycle */
  on?: Partial<{
    [E in Event]: EventListener<E>;
  }>;
  /** Custom WebSocket implementation */
  webSocketImpl?: unknown;
  /** Custom subscription ID generator */
  generateID?: (payload: SubscribePayload) => ID;
  /** JSON message reviver for parsing */
  jsonMessageReviver?: JSONMessageReviver;
  /** JSON message replacer for stringifying */
  jsonMessageReplacer?: JSONMessageReplacer;
}

Usage Examples:

import { createClient } from "graphql-ws";

// Basic client
const client = createClient({
  url: "ws://localhost:4000/graphql",
});

// Client with authentication
const authClient = createClient({
  url: "ws://localhost:4000/graphql",
  connectionParams: {
    authToken: "your-auth-token",
  },
});

// Client with retry configuration
const resilientClient = createClient({
  url: "ws://localhost:4000/graphql",
  retryAttempts: 5,
  retryWait: async (retries) => {
    await new Promise(resolve => setTimeout(resolve, Math.pow(2, retries) * 1000));
  },
  shouldRetry: (err) => {
    // Retry on connection errors but not on auth failures
    return err instanceof CloseEvent && err.code !== CloseCode.Unauthorized;
  },
});

Client Interface

Main client interface providing subscription management and event handling.

interface Client extends Disposable {
  /** Register event listener and return unsubscribe function */
  on<E extends Event>(event: E, listener: EventListener<E>): () => void;
  /** Subscribe to GraphQL operation and return unsubscribe function */
  subscribe<Data = Record<string, unknown>, Extensions = unknown>(
    payload: SubscribePayload,
    sink: Sink<FormattedExecutionResult<Data, Extensions>>
  ): () => void;
  /** Subscribe using async iterator pattern */
  iterate<Data = Record<string, unknown>, Extensions = unknown>(
    payload: SubscribePayload
  ): AsyncIterableIterator<FormattedExecutionResult<Data, Extensions>>;
  /** Terminate connection immediately */
  terminate(): void;
  /** Dispose client and clean up resources */
  dispose(): void;
}

Usage Examples:

// Subscription with callback pattern
const unsubscribe = client.subscribe(
  {
    query: `
      subscription MessageAdded($channel: String!) {
        messageAdded(channel: $channel) {
          id
          content
          user
          timestamp
        }
      }
    `,
    variables: { channel: "general" },
  },
  {
    next: (result) => {
      console.log("New message:", result.data?.messageAdded);
    },
    error: (err) => {
      console.error("Subscription error:", err);
    },
    complete: () => {
      console.log("Subscription completed");
    },
  }
);

// Subscription with async iterator
for await (const result of client.iterate({
  query: "subscription { notifications { id message } }",
})) {
  console.log("Notification:", result.data?.notifications);
}

Event System

Comprehensive event system for monitoring connection lifecycle and protocol messages.

type Event = 
  | "connecting"
  | "opened" 
  | "connected"
  | "ping"
  | "pong"
  | "message"
  | "closed"
  | "error";

type EventListener<E extends Event> = E extends "connecting"
  ? (isRetry: boolean) => void
  : E extends "opened"
  ? (socket: unknown) => void
  : E extends "connected"
  ? (socket: unknown, payload?: Record<string, unknown>, wasRetry?: boolean) => void
  : E extends "ping"
  ? (received: boolean, payload: PingMessage['payload']) => void
  : E extends "pong"
  ? (received: boolean, payload: PongMessage['payload']) => void
  : E extends "message"
  ? (message: Message) => void
  : E extends "closed"
  ? (event: CloseEvent) => void
  : E extends "error"
  ? (error: unknown) => void
  : never;

Usage Examples:

// Monitor connection state
client.on("connecting", () => {
  console.log("Connecting to server...");
});

client.on("connected", (socket, payload) => {
  console.log("Connected successfully", payload);
});

client.on("closed", (event) => {
  console.log(`Connection closed: ${event.code} ${event.reason}`);
});

client.on("error", (error) => {
  console.error("Client error:", error);
});

// Monitor protocol messages
client.on("ping", (pingMessage) => {
  console.log("Received ping");
});

client.on("message", (message) => {
  console.log("Protocol message:", message.type);
});

Subscription Payload

Structure for GraphQL operation requests sent over the WebSocket connection.

interface SubscribePayload {
  /** GraphQL operation string (query, mutation, or subscription) */
  query: string;
  /** Operation name for multi-operation documents */
  operationName?: string;
  /** Variables for the GraphQL operation */
  variables?: Record<string, unknown>;
  /** Additional extensions */
  extensions?: Record<string, unknown>;
}

Result Sink

Observer pattern interface for handling subscription results.

interface Sink<T = unknown> {
  /** Called for each result value */
  next(value: T): void;
  /** Called when an error occurs */
  error(error: unknown): void;
  /** Called when the subscription completes */
  complete(): void;
}

Termination Handling

Special close event class for tracking client-initiated terminations.

class TerminatedCloseEvent extends Error implements CloseEvent {
  readonly name = "TerminatedCloseEvent";
  readonly message = "4499: Terminated";
  readonly code = 4499;
  readonly reason = "Terminated";
  readonly wasClean = false;
}

Usage Examples:

// Handle termination in event listener
client.on("closed", (event) => {
  if (event instanceof TerminatedCloseEvent) {
    console.log("Client was terminated manually");
  } else {
    console.log("Connection closed by server or network");
  }
});

// Terminate when needed
client.terminate();

Advanced Usage Patterns

Conditional Reconnection

const client = createClient({
  url: "ws://localhost:4000/graphql",
  retryAttempts: 3,
  shouldRetry: (errOrCloseEvent) => {
    // Don't retry on authentication errors
    if (errOrCloseEvent instanceof CloseEvent) {
      return errOrCloseEvent.code !== CloseCode.Unauthorized;
    }
    return true;
  },
});

Dynamic Connection Parameters

const client = createClient({
  url: "ws://localhost:4000/graphql",
  connectionParams: async () => {
    const token = await getAuthToken();
    return { authorization: `Bearer ${token}` };
  },
});

Custom WebSocket Implementation

import WebSocket from "ws";

const client = createClient({
  url: "ws://localhost:4000/graphql",
  webSocketImpl: WebSocket,
});