Skip to main content

Realtime

Labkit realtime support is centered on GraphQL subscriptions over graphql-ws, with browser recovery handled by @omgjs/labkit-webapp-realtime.

The important constraint is that websocket auth is established when the socket connects. A token update in memory does not change the already-open connection. The client must reconnect.

Server Responsibilities

@omgjs/labkit-server-graphql builds GraphQL websocket configuration for the Nest Apollo module:

  • reads websocket authorization from connection params;
  • stores a connection id and principal in websocket extra;
  • reuses the same principal shape as HTTP GraphQL context;
  • logs connect, disconnect, and subscribe events when subscription logging is enabled.

@omgjs/labkit-server-auth can create a GraphQL module that verifies bearer access tokens and maps them into Labkit principals.

The app still owns pub/sub implementation. For one server process, in-memory pub/sub can be enough. For multiple server instances, use a shared backend such as Redis so events published by one instance can reach subscriptions connected to another instance.

Browser Runtime

DefaultWebappRealtimeConnection is the recommended browser runtime. It owns the low-level websocket lifecycle and exposes a stable GraphQL WS compatible client through getClient().

Relay and application code keep the same outer client object. Internally Labkit can replace the concrete graphql-ws client when terminate-only recovery is not enough, then resubscribe active operations.

The default runtime handles:

  • connection states: idle, connecting, connected, retrying, disconnected;
  • heartbeat interval and timeout;
  • reconnect watchdog escalation from terminate() to inner client recreation;
  • fatal close-code handling;
  • browser online/offline and visible/resume recovery;
  • active subscription resubscription after internal client recreation;
  • user-facing connection messages.

State Monitoring

Applications observe connection state; they do not own recovery decisions. When Relay is present, prefer observing the runtime from DefaultWebappRelayRuntime so UI monitors the same realtime connection Relay uses.

import { relayRuntime } from "./relay/environment";

const realtime = relayRuntime.getRealtime();
const client = realtime.getClient();
const state = relayRuntime.getRealtimeConnectionState();
const unsubscribe = relayRuntime.subscribeToRealtimeConnectionState(
(nextState) => {
console.log(nextState.status, nextState.detail);
},
);

Product UI can use getRealtimeConnectionMessage for a first status banner, or render richer state from getConnectionState().

Relay Integration

@omgjs/labkit-webapp-graphql-relay provides a default runtime that creates the Relay environment and realtime connection together:

import { DefaultWebappRelayRuntime } from "@omgjs/labkit-webapp-graphql-relay";

const relayRuntime = new DefaultWebappRelayRuntime({
httpEndpoint: HTTP_ENDPOINT,
wsEndpoint: WS_ENDPOINT,
auth,
});

const environment = relayRuntime.getEnvironment();
const realtime = relayRuntime.getRealtime();

The runtime uses auth-session expiry to refresh before websocket reconnects. Advanced apps can still pass a custom realtime adapter to createWebappRelayEnvironment.

Runtime Notes

Tune heartbeat, timeout, and reconnect settings only after observing real network behavior. The defaults are meant to cover normal browser sleep, deployment reconnects, and transient network loss without application code knowing how to repair the transport.