or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

common.mddocs/

Common Types & Protocol

Shared types, constants, and utilities used by both client and server implementations, providing the complete GraphQL over WebSocket Protocol message types and processing functions.

Capabilities

Protocol Constants

Core protocol identifiers and version information.

/** The WebSocket sub-protocol used for GraphQL over WebSocket Protocol */
const GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws";

/** Deprecated subprotocol used by subscriptions-transport-ws (private) */
const DEPRECATED_GRAPHQL_WS_PROTOCOL = "graphql-ws";

Usage Examples:

import { GRAPHQL_TRANSPORT_WS_PROTOCOL } from "graphql-ws";

// WebSocket client connection
const ws = new WebSocket("ws://localhost:4000/graphql", GRAPHQL_TRANSPORT_WS_PROTOCOL);

// Server subprotocol handling
const server = new WebSocketServer({
  handleProtocols: (protocols) => {
    return protocols.includes(GRAPHQL_TRANSPORT_WS_PROTOCOL) 
      ? GRAPHQL_TRANSPORT_WS_PROTOCOL 
      : false;
  },
});

Close Codes

Standard close codes for different error and termination conditions as defined by the GraphQL over WebSocket Protocol.

enum CloseCode {
  /** Internal server error */
  InternalServerError = 4500,
  /** Internal client error */
  InternalClientError = 4005,
  /** Bad request - malformed message or invalid payload */
  BadRequest = 4400,
  /** Bad response from server */
  BadResponse = 4004,
  /** Unauthorized - tried subscribing before connect ack */
  Unauthorized = 4401,
  /** Forbidden - insufficient permissions */
  Forbidden = 4403,
  /** Subprotocol not acceptable */
  SubprotocolNotAcceptable = 4406,
  /** Connection initialization timeout */
  ConnectionInitialisationTimeout = 4408,
  /** Connection acknowledgment timeout */
  ConnectionAcknowledgementTimeout = 4504,
  /** Subscriber already exists - subscriber distinction is very important */
  SubscriberAlreadyExists = 4409,
  /** Too many initialization requests */
  TooManyInitialisationRequests = 4429,
}

Usage Examples:

import { CloseCode } from "graphql-ws";

// Client error handling
client.on("closed", (event) => {
  switch (event.code) {
    case CloseCode.Unauthorized:
      console.log("Authentication failed");
      redirectToLogin();
      break;
    case CloseCode.Forbidden:
      console.log("Insufficient permissions");
      showPermissionError();
      break;
    case CloseCode.BadRequest:
      console.log("Malformed request");
      break;
    default:
      console.log("Connection closed:", event.code, event.reason);
  }
});

// Server closing connection
server.close(CloseCode.Unauthorized, "Invalid token");

Message Types

Protocol message types for the GraphQL over WebSocket communication.

enum MessageType {
  /** Client -> Server: Initialize connection */
  ConnectionInit = "connection_init",
  /** Server -> Client: Acknowledge connection */
  ConnectionAck = "connection_ack", 
  /** Bidirectional: Ping for keep-alive */
  Ping = "ping",
  /** Bidirectional: Pong response to ping */
  Pong = "pong",
  /** Client -> Server: Start subscription/query/mutation */
  Subscribe = "subscribe",
  /** Server -> Client: Send result data */
  Next = "next",
  /** Server -> Client: Send error */
  Error = "error",
  /** Server -> Client: Complete operation */
  Complete = "complete",
}

Core Type Definitions

Fundamental types used throughout the GraphQL over WebSocket implementation.

/** Globally unique ID for identifying subscriptions */
type ID = string;

/** Function for transforming values during JSON parsing */
type JSONMessageReviver = (this: any, key: string, value: any) => any;

/** Function for customizing JSON string production */
type JSONMessageReplacer = (this: any, key: string, value: any) => any;

Disposable Pattern

Interface for resources that need cleanup.

interface Disposable {
  /** Dispose of the resource and perform cleanup */
  dispose(): void;
}

Usage Examples:

import { createClient } from "graphql-ws";

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

// Client implements Disposable
process.on("SIGTERM", () => {
  client.dispose(); // Clean up resources
});

Observer Pattern

Stream representation interface for handling values over time.

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

Usage Examples:

const sink: Sink<ExecutionResult> = {
  next: (result) => {
    console.log("Received result:", result.data);
  },
  error: (err) => {
    console.error("Stream error:", err);
  },
  complete: () => {
    console.log("Stream completed");
  },
};

client.subscribe({ query: "subscription { messages }" }, sink);

Message Structures

Protocol message interfaces for client-server communication.

interface ConnectionInitMessage {
  readonly type: "connection_init";
  readonly payload?: Record<string, unknown>;
}

interface ConnectionAckMessage {
  readonly type: "connection_ack";
  readonly payload?: Record<string, unknown>;
}

interface PingMessage {
  readonly type: "ping";
  readonly payload?: Record<string, unknown>;
}

interface PongMessage {
  readonly type: "pong";
  readonly payload?: Record<string, unknown>;
}

interface SubscribeMessage {
  readonly type: "subscribe";
  readonly id: ID;
  readonly payload: SubscribePayload;
}

interface NextMessage {
  readonly type: "next";
  readonly id: ID;
  readonly payload: FormattedExecutionResult;
}

interface ErrorMessage {
  readonly type: "error";
  readonly id: ID;
  readonly payload: readonly GraphQLFormattedError[];
}

interface CompleteMessage {
  readonly type: "complete";
  readonly id: ID;
}

type Message<T extends MessageType = MessageType> = T extends "connection_init"
  ? ConnectionInitMessage
  : T extends "connection_ack"
  ? ConnectionAckMessage
  : T extends "ping"
  ? PingMessage
  : T extends "pong"
  ? PongMessage
  : T extends "subscribe"
  ? SubscribeMessage
  : T extends "next"
  ? NextMessage
  : T extends "error"
  ? ErrorMessage
  : T extends "complete"
  ? CompleteMessage
  : never;

GraphQL Execution Types

Types for GraphQL operation payloads and results.

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>;
}

interface ExecutionResult<Data = Record<string, unknown>, Extensions = unknown> {
  /** Operation result data */
  data?: Data | null;
  /** GraphQL errors that occurred during execution */
  errors?: ReadonlyArray<GraphQLError>;
  /** Indicates if there are more results coming */
  hasNext?: boolean;
  /** Extensions from execution */
  extensions?: Extensions;
}

interface FormattedExecutionResult<Data = Record<string, unknown>, Extensions = unknown> {
  /** Operation result data */
  data?: Data | null;
  /** Formatted GraphQL errors */
  errors?: ReadonlyArray<GraphQLFormattedError>;
  /** Indicates if there are more results coming */
  hasNext?: boolean;
  /** Extensions from execution */
  extensions?: Extensions;
}

Message Processing

Functions for validating, parsing, and stringifying protocol messages.

/**
 * Validates a value as a protocol-compliant message
 * @param val - Value to validate
 * @returns Validated message
 * @throws Error if validation fails
 */
function validateMessage(val: unknown): Message;

/**
 * Parses raw WebSocket message data into protocol message
 * @param data - Raw message data
 * @param reviver - Optional JSON reviver function
 * @returns Parsed protocol message
 * @throws Error if parsing fails
 */
function parseMessage(data: unknown, reviver?: JSONMessageReviver): Message;

/**
 * Stringifies protocol message for sending over WebSocket
 * @param msg - Protocol message to stringify
 * @param replacer - Optional JSON replacer function
 * @returns Stringified message
 */
function stringifyMessage<T extends MessageType>(
  msg: Message<T>,
  replacer?: JSONMessageReplacer
): string;

Usage Examples:

import { parseMessage, stringifyMessage, validateMessage } from "graphql-ws";

// Parse incoming WebSocket message
websocket.onmessage = (event) => {
  try {
    const message = parseMessage(event.data);
    console.log("Received:", message.type);
    
    // Validate message structure
    validateMessage(message);
    
    // Handle message...
  } catch (error) {
    console.error("Invalid message:", error);
  }
};

// Send outgoing message
const subscribeMsg = {
  type: "subscribe" as const,
  id: "sub_1",
  payload: {
    query: "subscription { messages }",
  },
};

websocket.send(stringifyMessage(subscribeMsg));

Protocol Message Flow

The GraphQL over WebSocket Protocol defines a specific message flow:

Connection Establishment

  1. Client: Sends connection_init with optional connection parameters
  2. Server: Validates parameters and responds with connection_ack
  3. Connection: Ready for GraphQL operations

GraphQL Operations

  1. Client: Sends subscribe with GraphQL operation
  2. Server: Executes operation and sends:
    • next messages with results (for subscriptions/queries)
    • error messages if errors occur
    • complete message when operation finishes

Keep-Alive

  • Either: Can send ping messages
  • Recipient: Should respond with pong messages

Connection Termination

  • Either: Can close connection with appropriate close code
  • Server: Should clean up active subscriptions

Complete Example:

// Message flow example
const messages = [
  // 1. Connection initialization
  { type: "connection_init", payload: { authToken: "..." } },
  { type: "connection_ack" },
  
  // 2. Start subscription
  { 
    type: "subscribe", 
    id: "sub_1", 
    payload: { query: "subscription { messageAdded { id content } }" }
  },
  
  // 3. Receive results
  { 
    type: "next", 
    id: "sub_1", 
    payload: { data: { messageAdded: { id: "1", content: "Hello" } } }
  },
  { 
    type: "next", 
    id: "sub_1", 
    payload: { data: { messageAdded: { id: "2", content: "World" } } }
  },
  
  // 4. Keep-alive
  { type: "ping" },
  { type: "pong" },
  
  // 5. Complete subscription
  { type: "complete", id: "sub_1" },
];