GuidesComponents

Socket

Real-time socket connection to the Ovok backend - SocketProvider, useSocket hook, and AI event listener

  • Stable unknown
  • Release candidate since 1.16.1-rc.3
  • Development since 1.16.1-dev.2111+9796905

The Socket module wraps a socket.io-client connection to the Ovok backend and exposes the live Socket instance through React context. It connects when the native next client has a session, disconnects on sign-out, and reconnects the same socket when network access returns.

This is the channel real-time SDK features (AI streaming, server-pushed logs, presence) ride on. Most apps only need it if they consume one of those features; REST traffic uses the native Ovok client.

Exports

// from @ovok/native/socket
export { SocketProvider } from "./components/socket-provider";
export { SocketContext } from "./contexts/socket-context";
export type { SocketContextType } from "./types/socket-context-type";
export type { SocketLogPayload } from "./types/socket-log-payload";
export { useAiEventListener } from "./hooks/use-ai-event-listener";
export { useSocket } from "./hooks/use-socket";

Source: src/modules/socket/index.ts.

<SocketProvider>

Wrap your app (inside OvokProvider so the client + access token are available) to open the socket connection.

Props

import type { SocketLogPayload } from "@ovok/native/socket";

type SocketProviderProps = React.PropsWithChildren<{
  onError?: (error: Error) => void;
  onConnectionError?: (error: Error) => void;
  onPing?: (latency: number) => void;
  onConnect?: () => void;
  onDisconnect?: () => void;
  onLogs?: (message: SocketLogPayload) => void;
}>;
PropWhen it fires
onConnectSocket transport opened and authenticated with the bearer token.
onDisconnectSocket transport closed (network change, manual disconnect, idle timeout).
onErrorerror event from the socket — typically protocol-level failures.
onConnectionErrorconnect_error event — the handshake or authentication failed. Both onError and onConnectionError flip internal state back to disconnected.
onPingSocket.IO ping event latency/value supplied by the client transport.
onLogsServer-pushed log line. SocketLogPayload = { message: string; level: 'info' | 'warn' | 'error' }.

SocketProvider reads the native client's baseUrl and active session access token from OvokProvider, so it must be a descendant of that provider. The connection URL is ${baseUrl}events.

Reconnection

SocketProvider registers a NetInfo listener. When the network goes offline it calls socket.disconnect(). When the network returns it reconnects that socket with its listeners intact. An account switch closes the previous socket before connecting with the new token. Default socket.io-client options are 5 reconnection attempts with 1s → 5s delay.

useSocket()

Read the live socket + connection state from context.

type SocketContextType = {
  socket: ReturnType<typeof io> | null;
  isConnected: boolean;
};

const { socket, isConnected } = useSocket();

Throws if called outside <SocketProvider>. Returned socket is null without a session; gate connected operations on isConnected.

useAiEventListener()

Subscribe to AI events streamed over the socket (currently the /ai/session/message channel).

type MessageEvent = {
  content: string;
  mode: "user" | "assistant";
  reference: string;
  sender: string;
  sentAt: string;
  session: string;
};

const { subscribe } = useAiEventListener();

Use:

import { useAiEventListener } from "@ovok/native/socket";

function ChatView() {
  const { subscribe } = useAiEventListener();
  const [messages, setMessages] = React.useState<MessageEvent[]>([]);

  React.useEffect(() => {
    const unsubscribe = subscribe("message", (event) => {
      setMessages((prev) => [...prev, event as MessageEvent]);
    });
    return () => unsubscribe?.();
  }, [subscribe]);

  return <ChatList messages={messages} />;
}

subscribe(type, callback) returns an unsubscribe function. Multiple subscribers per event type are supported; each receives every event independently.

Full integration example

import { OvokProvider } from "@ovok/core/next/react";
import {
  ThemeProvider as OvokThemeProvider,
  DEFAULT_COLORS,
} from "@ovok/native";
import { Ovok } from "@ovok/native/next";
import { SocketProvider } from "@ovok/native/socket";
import * as SecureStore from "expo-secure-store";

const client = new Ovok({
  baseUrl: "https://api.ovok.com/",
  tenantCode: "your-tenant",
  secureStore: SecureStore,
});

function App() {
  return (
    <OvokThemeProvider theme={{ colors: DEFAULT_COLORS, dark: false }}>
      <OvokProvider client={client}>
        <SocketProvider
          onConnect={() => console.log("socket up")}
          onDisconnect={() => console.log("socket down")}
          onConnectionError={(e) => console.error("handshake failed", e)}
          onLogs={(payload) => console.log(`[${payload.level}]`, payload.message)}
        >
          <YourAppRoot />
        </SocketProvider>
      </OvokProvider>
    </OvokThemeProvider>
  );
}

When to mount it

  • Mount once at the root, inside OvokProvider. Multiple SocketProvider instances create multiple sockets, which counts against the backend's per-user connection limit.
  • Mount only if you consume AI events, server-pushed logs, or another socket-only feature. REST traffic via useFhirSearch / client.create does not need a socket.
  • Mount before any consumer of useSocket or useAiEventListener in the tree — these throw if called outside.

Last updated on

On this page