Effection Logo
@effection-contrib/websocketv1.0.1thefrontside/effection-contrib
JSR BadgeNPM Badge with published versionBundle size badgeDependency count badgeTree shaking support badge
import { } from "@effection-contrib/websocket"

WebSocket

Use the WebSocket API as an Effection resource. Instead of a fragile, spring-loaded confederation of 'open', 'close', 'error', and 'message' event handlers, useWebSocket() organizes them for you so that you can consume all events from the server as a plain stream that has state-readiness and proper error handling baked in.

To use a websocket import the useWebSocket() operation which behaves just like the WebSocket constructor.

import { each, main } from "effection";
import { useWebSocket } from "@effection-contrib/websocket";

await main(function* () {
  let socket = yield* useWebSocket("ws://websocket.example.org");

  socket.send("Hello World");

  for (let message of yield* each(socket)) {
    console.log("Message from server", message);
    yield* each.next();
  }
});

The resource provides the following niceties:

  • When useWebSocket() returns, it will have already received the open event.
  • If the socket recieves an error event, that event's error will be thrown to the current error boundary.
  • The socket is a stream whose items are each MessageEvents, the CloseEvent of the websocket will be the close event of that stream.

You can also instantiate a websocket separately and pass it along to useWebSocket(). This is helpful for runtimes such as NodeJS prior to version 21 that do not have built in support for websocket.

import { createWebSocket } from "my-websocket-client";

await main(function* () {
  let socket = yield* useWebSocket(() =>
    createWebSocket("ws://websocket.example.org")
  );

  for (let message of yield* each(socket)) {
    console.log("Message from server", message);
    yield* each.next();
  }
});

API

interface WebSocketResource<T> extends Stream<MessageEvent<T>, CloseEvent> {

  • the type of data that this websocket accepts

    binaryType: BinaryType;
  • bufferedAmmount: number;
  • extensions: string;
  • protocol: string;
  • readyState: number;
  • url: string;
  • send(data: WebSocketData): void;
}

Handle to a WebSocket object that can be consumed as an Effection stream. It has all the same properties as the underlying WebSocket apart from the event handlers. Instead, the resource itself is a subscribale stream. When the socket is closed, the stream will complete with a CloseEvent

A WebSocketResource does not have an explicit close method. Rather, the underlying socket will be automatically closed when the resource passes out of scope.

function useWebSocket(url: string, protocols?: string): Operation<WebSocketResource<T>>

Create a WebSocket resource using the native WebSocket constructor available on the current platform.

The resource will not be returned until a connection has been succesffuly established with the server and the open has been received. Once initialized, it will crash if it receives an error event at any time.

Once created, the websocket resource can be use to consume events from the server:

let socket = yield* useWebSocket("ws://websocket.example.org");

for (let event of yield* each(socket)) {
  console.log('event data: ', event.data);
  yield* each.next();
}

function useWebSocket(create: () => WebSocket): Operation<WebSocketResource<T>>

Create a WebSocket resource, but delegate the creation of the underlying websocket to a function of your choice. This is necessary on platforms that do not have a global WebSocket constructor such as NodeJS <= 20.

The resource will not be returned until a connection has been succesffuly established with the server and the open has been received. Once initialized, it will crash if it receives an error event at any time.

Once created, the websocket resource can be use to consume events from the server:

import * as ws from 'ws';

function* example() {
  let socket = yield* useWebSocket(() => new ws.WebSocket("ws://websocket.example.org"));

  for (let event of yield* each(socket)) {
    console.log('event data: ', event.data);
    yield* each.next();
  }
}