Skip to main content

Installation

npm install @insforge/sdk@latest
import { createClient } from '@insforge/sdk';

const insforge = createClient({
  baseUrl: 'https://your-app.insforge.app',
  anonKey: 'your-anon-key'  // Optional: for public/unauthenticated requests
});

Mental Model

The TypeScript SDK opens one Socket.IO connection to your InsForge backend. You subscribe to named channels, listen for event names, and optionally publish events back to channels you have joined. Events can come from two places:
  • Database triggers that call realtime.publish(channel, event, payload).
  • Clients that call insforge.realtime.publish(channel, event, payload).
For the backend channel and RLS model, see Realtime overview.

Quick Start

import { createClient } from '@insforge/sdk';

const insforge = createClient({
  baseUrl: 'https://your-app.insforge.app',
  anonKey: 'your-anon-key'
});

insforge.realtime.on('connect', () => {
  console.log('Connected:', insforge.realtime.socketId);
});

insforge.realtime.on('error', (error) => {
  console.error(error.code, error.message);
});

await insforge.realtime.connect();

const subscription = await insforge.realtime.subscribe('order:123');

if (!subscription.ok) {
  throw new Error(subscription.error.message);
}

insforge.realtime.on('status_changed', (message) => {
  console.log(message.status);
  console.log(message.meta.messageId);
});
Register connect, disconnect, connect_error, and error handlers before calling connect() so early connection failures are visible.

connect()

Establish a WebSocket connection.
await insforge.realtime.connect();
Returns:
Promise<void>
Notes:
  • The SDK includes the current auth token when one exists. If there is no signed-in user, it can use the configured anon key.
  • Multiple connect() calls while a connection is already in progress reuse the same connection promise.
  • The connection attempt times out after 10 seconds.

subscribe()

Subscribe to a channel and receive the current presence snapshot.
const response = await insforge.realtime.subscribe('chat:room-1');

if (response.ok) {
  console.log(response.channel);
  console.log(response.presence.members);
} else {
  console.error(response.error.code, response.error.message);
}
Parameters:
ParameterTypeDescription
channelstringResolved channel name, such as orders, order:123, or chat:room-1.
Returns:
type SubscribeResponse =
  | {
      ok: true;
      channel: string;
      presence: {
        members: PresenceMember[];
      };
    }
  | {
      ok: false;
      channel: string;
      error: {
        code: string;
        message: string;
      };
    };
subscribe() auto-connects if needed. Calling connect() explicitly is still recommended so connection event handlers are already attached.

publish()

Publish an event to a channel.
await insforge.realtime.publish('chat:room-1', 'new_message', {
  text: 'Hello',
  sentAt: new Date().toISOString()
});
Parameters:
ParameterTypeDescription
channelstringChannel to publish to. The client must already be subscribed.
eventstringEvent name that subscribers listen for.
payloadRecord<string, unknown>JSON-serializable message payload.
Publishing requires a prior successful subscription to the same channel. If RLS is enabled on realtime.messages, publish is also checked against INSERT policies.
Publish failures are emitted through the error event.

on()

Listen for custom events, connection events, presence events, and realtime errors.
insforge.realtime.on('new_message', (message) => {
  console.log(message.text);
  console.log(message.meta.senderType);
});
Reserved events:
EventPayloadDescription
connectNoneThe WebSocket connected.
connect_errorErrorInitial connection or reconnect failed.
disconnectstringThe WebSocket disconnected.
errorRealtimeErrorPayloadSubscribe or publish failed.
presence:joinPresenceJoinMessageA logical member became present in a channel.
presence:leavePresenceLeaveMessageA logical member is no longer present in a channel.

once()

Listen for an event once, then remove the listener automatically.
insforge.realtime.once('checkout_completed', (message) => {
  console.log('Completed:', message.orderId);
});

off()

Remove an event listener.
function handleStatus(message: OrderStatusMessage) {
  console.log(message.status);
}

insforge.realtime.on('status_changed', handleStatus);
insforge.realtime.off('status_changed', handleStatus);

unsubscribe()

Leave a channel.
insforge.realtime.unsubscribe('chat:room-1');
unsubscribe() is fire-and-forget. If this was the final socket for a logical member, other subscribers receive presence:leave.

disconnect()

Close the WebSocket and clear local subscriptions.
insforge.realtime.disconnect();

Message Shape

Delivered messages include your payload fields plus server metadata.
import type { SocketMessage } from '@insforge/sdk';

interface OrderStatusMessage extends SocketMessage {
  id: string;
  status: string;
}

insforge.realtime.on<OrderStatusMessage>('status_changed', (message) => {
  console.log(message.id);
  console.log(message.status);
  console.log(message.meta.messageId);
  console.log(message.meta.senderType);
  console.log(message.meta.senderId);
  console.log(message.meta.timestamp);
});
Metadata:
interface SocketMessageMeta {
  channel?: string;
  messageId: string;
  senderType: 'system' | 'user';
  senderId?: string;
  timestamp: string;
}
senderType is system for database-triggered messages and user for client-published messages.

Presence

A successful subscription returns the current presence snapshot.
const response = await insforge.realtime.subscribe('chat:room-1');

if (response.ok) {
  for (const member of response.presence.members) {
    console.log(member.type, member.presenceId, member.joinedAt);
  }
}
Presence member:
type PresenceMember =
  | {
      type: 'user';
      presenceId: string;
      joinedAt: string;
    }
  | {
      type: 'anonymous';
      presenceId: string;
      joinedAt: string;
    };
Listen for changes:
insforge.realtime.on('presence:join', (message) => {
  console.log('Joined:', message.member.presenceId);
});

insforge.realtime.on('presence:leave', (message) => {
  console.log('Left:', message.member.presenceId);
});

Properties

PropertyTypeDescription
isConnectedbooleanWhether the socket is currently connected.
connectionState'disconnected' | 'connecting' | 'connected'Current SDK connection state.
socketIdstring | undefinedSocket.IO ID when connected.
getSubscribedChannels()() => string[]Local list of subscribed channels.

Error Handling

insforge.realtime.on('connect_error', (error) => {
  console.error('Connection failed:', error.message);
});

insforge.realtime.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
});

insforge.realtime.on('error', (error) => {
  console.error(error.channel, error.code, error.message);
});
Common realtime error codes:
CodeMeaning
REALTIME_UNAUTHORIZEDRLS or channel matching denied subscribe or publish.
REALTIME_NOT_SUBSCRIBEDThe client tried to publish before subscribing to the channel.
INTERNAL_ERRORThe backend could not complete the realtime operation.

Complete Example

const channel = `order:${orderId}`;

insforge.realtime.on('error', (error) => {
  console.error(error.code, error.message);
});

await insforge.realtime.connect();

const response = await insforge.realtime.subscribe(channel);

if (!response.ok) {
  throw new Error(response.error.message);
}

insforge.realtime.on('status_changed', (message) => {
  renderOrderStatus(message.status);
});

await insforge.realtime.publish(channel, 'customer_viewed', {
  orderId,
  viewedAt: new Date().toISOString()
});

function cleanup() {
  insforge.realtime.unsubscribe(channel);
  insforge.realtime.disconnect();
}