Skip to content
effect-api-query

WebSocket ready clients

Acquire a shared RPC connection and keep cancellation and replacement application-owned.

Acquire a flat Effect RPC client inside an application Scope. Pass it to createRpcQueryUtils. The same connection can carry concurrent unary requests, accumulated streamed queries, and live queries. Each request retains its own query key and cancellation.

Keep the flat ready client and its parent Scope alive. The complete acquisition recipe owns cleanup for the socket, protocol, client, and QueryClient. In that application setup:

const rpc = createRpcQueryUtils(socketGroup, { client, keyPrefix: ['socket', owner] })

The host supplies Socket.layerWebSocketConstructorGlobal in browsers or NodeSocket.layerWebSocketConstructorWS in Node. The server uses the same group and serialization through RpcServer.toHttpEffectWebsocket. This recipe uses JSON.

Use generated queryOptions for unary reads and native observers for streamedOptions or liveOptions. Independent keys share the connection without sharing cancellation. The first stream emission becomes successful data while fetchStatus remains fetching.

In a server request loader, await fetchStreamSnapshot(queryClient, options, controls) with the generated history or live options. For this recipe, input is { channel: 'clock' }. History uses rpc.values.watch.streamedOptions({ input, maxChunks: 20 }), while latest data uses rpc.values.watch.liveOptions({ input }). Each capture has timeoutMs: 5000. Capture waits for local cleanup before disposal.

The complete typed snapshot recipe includes concurrent reads and disposal. Stream Snapshots explains exclusive capture and local finalizer drain. maxChunks bounds cached history. rpcOptions.streamBufferSize bounds the transport queue. They govern different resources.

Cancel one stream with its generated exact key, for example cancelQueries({ queryKey: options.queryKey, exact: true }). Other stream keys and concurrent unary calls continue on the same connection. TanStack retains its native cancellation result: after a stream has published data, cancellation can settle its query promise successfully with cached data.

Drain local work. Call application.dispose before disposing the server or an enclosing runtime. The registered finalizer cancels queries and clears their cache before the client and socket close. To replace an owner, await disposal. Acquire a new application Scope and ready client. Construct a new QueryClient.

The same semantic prefix can produce the same keys while resource lifetimes and cache instances remain independent. Follow owner-switching rules when authentication changes.

Replacement acquires a new connection. Transport reconnection, backoff, credentials, event replay, and whether failed queries retry belong to the application and Effect’s transport configuration. Reconnection does not imply that a cancelled stream resumes from its last cached event.

The WebSocket transport forwards an interrupted request to the RPC server. Cooperative handler finalizers were observed on this transport for both stream views. A local abort signal or idle query state alone cannot prove remote completion. If the application must wait for server work to stop, use a domain acknowledgement. Closing a connection immediately after cancellation can race delivery of that interrupt.

An independent server interruption remains an EffectRpcQueryError, even when TanStack has not cancelled the query. Its Cause preserves the failure received by the ready client. Wire serialization can change the original server’s Cause representation. A failed stream leaves already published data available alongside its error.

Keep streaming success schemas compatible on both ends. A chunk that fails client Schema decoding ends the affected request.

Query cancellation cooperatively stops future work. It does not undo completed writes. If the domain needs an explicit cancellation operation, use an explicit cancellable command.