method

WebSocketHandler.error

error: Error,
): void | Promise<void>;

Called when one of the other handlers throws. Without it, the error is an uncaught exception.

Not called when an async handler rejects. That's an unhandled rejection.

@param error

The value that was thrown

@param ws

The websocket whose handler threw

Referenced types

interface Error

interface ServerWebSocket<T = undefined>

A fast WebSocket designed for servers.

Features:

  • Message compression - Messages can be compressed
  • Backpressure - send() reports when the client is not ready to receive data.
  • Dropped messages - send() reports when the client cannot receive data.
  • Topics - Messages can be ServerWebSocket.published to a specific topic and the client can ServerWebSocket.subscribe to topics

This differs slightly from the browser WebSocket, which Bun supports for clients.

Powered by uWebSockets.

Bun.serve({
  websocket: {
    open(ws) {
      console.log("Connected", ws.remoteAddress);
    },
    message(ws, data) {
      console.log("Received", data);
      ws.send(data);
    },
    close(ws, code, reason) {
      console.log("Disconnected", code, reason);
    },
  }
});
  • binaryType?: 'arraybuffer' | 'blob' | 'uint8array' | 'nodebuffer'

    Sets how binary data is returned in events.

    • if nodebuffer, binary data is returned as Buffer objects. (default)
    • if arraybuffer, binary data is returned as ArrayBuffer objects.
    • if uint8array, binary data is returned as Uint8Array objects.
    • if blob, binary data is returned as Blob objects.

    This applies to binary messages and to the payloads of pings and pongs.

    Bun.serve({
      websocket: {
        open(ws) {
          ws.binaryType = "uint8array";
        },
        message(ws, message) {
          console.log(message instanceof Uint8Array); // true, for a binary message
        },
      },
      // ...
    });
  • data: T

    Custom data you can assign to a client. It can be read and written at any time.

    import { serve } from "bun";
    
    serve({
      fetch(request, server) {
        const data = {
          accessToken: request.headers.get("Authorization"),
        };
        if (server.upgrade(request, { data })) {
          return;
        }
        return new Response();
      },
      websocket: {
        data: {} as {accessToken: string | null},
        message(ws) {
          console.log(ws.data.accessToken);
        }
      }
    });
  • readonly isPaused: boolean

    Whether the socket is paused. Always false on a closed socket.

  • readonly readyState: WebSocketReadyState

    The ready state of the client.

    • if 1, the client is connected.
    • if 3, the client is closed, or close() or terminate() was called.

    A ServerWebSocket is never 0 (connecting) or 2 (closing).

    console.log(socket.readyState); // 1
  • readonly remoteAddress: string

    The IP address of the client.

    console.log(socket.remoteAddress); // "127.0.0.1"
  • readonly subscriptions: string[]

    Returns an array of all topics the client is currently subscribed to.

    ws.subscribe("chat");
    ws.subscribe("notifications");
    console.log(ws.subscriptions); // ["chat", "notifications"]
  • code?: number,
    reason?: string
    ): void;

    Closes the connection.

    Close codes:

    • 1000 means "normal closure" (default)
    • 1009 means a message was too big and was rejected
    • 1011 means the server encountered an error
    • 1012 means the server is restarting
    • 1013 means the server is too busy or the client is rate-limited
    • 3000 through 3999 are reserved for libraries and frameworks
    • 4000 through 4999 are reserved for applications

    0, 1005 and 1006 send a Close frame with no code, so you can forward the code from a close handler unchanged.

    To close the connection abruptly, use terminate().

    @param code

    The close code to send

    @param reason

    The close reason to send. Truncated to 123 bytes of UTF-8, at a character boundary.

  • cork<R = unknown>(
    callback: (ws: ServerWebSocket<T>) => R
    ): R;

    Batches send() and publish() operations, which makes it faster to send data.

    The message, open, and drain callbacks are automatically corked, so you only need to call this if you are sending messages outside of those callbacks or in async functions.

    @param callback

    The callback to run. It runs even if the socket is closed.

    @returns

    The return value of callback.

    ws.cork((ctx) => {
      ctx.send("These messages");
      ctx.sendText("are sent");
      ctx.sendBinary(new TextEncoder().encode("together!"));
    });
  • The number of bytes queued by send() or publish() that haven't been written to the socket yet.

  • topic: string
    ): boolean;

    Returns whether the client is subscribed to the topic.

    @param topic

    The topic name.

    ws.subscribe("chat");
    console.log(ws.isSubscribed("chat")); // true
  • pause(): void;

    Stops reading from the socket, so the client sees TCP backpressure. The idle timeout is suspended until resume(). Sending still works.

    Messages already read from the socket are still delivered.

    message(ws, message) {
      if (!file.write(message)) {
        ws.pause();
        file.once("drain", () => ws.resume());
      }
    }
  • data?: string | Blob | BufferSource
    ): number;

    Sends a ping.

    @param data

    The data to send

  • data?: string | Blob | BufferSource
    ): number;

    Sends a pong.

    @param data

    The data to send

  • topic: string,
    data: string | Blob | BufferSource,
    compress?: boolean
    ): number;

    Sends a message to subscribers of the topic.

    @param topic

    The topic name.

    @param data

    The data to send.

    @param compress

    Whether to compress the data. Defaults to false. Ignored if the client does not support compression

    ws.publish("chat", "Hello!");
    ws.publish("chat", "Compress this.", true);
    ws.publish("chat", new Uint8Array([1, 2, 3, 4]));
  • topic: string,
    compress?: boolean
    ): number;

    Sends a binary message to subscribers of the topic.

    @param topic

    The topic name.

    @param data

    The data to send.

    @param compress

    Whether to compress the data. Defaults to false. Ignored if the client does not support compression

    ws.publishBinary("chat", new TextEncoder().encode("Hello!"));
    ws.publishBinary("chat", new Uint8Array([1, 2, 3, 4]), true);
  • topic: string,
    data: string,
    compress?: boolean
    ): number;

    Sends a text message to subscribers of the topic.

    @param topic

    The topic name.

    @param data

    The data to send.

    @param compress

    Whether to compress the data. Defaults to false. Ignored if the client does not support compression

    ws.publishText("chat", "Hello!");
    ws.publishText("chat", "Compress this.", true);
  • resume(): void;

    Starts reading from the socket again after pause().

  • data: string | Blob | BufferSource,
    compress?: boolean
    ): number;

    Sends a message to the client.

    @param data

    The data to send.

    @param compress

    Whether to compress the data. Defaults to false. Ignored if the client does not support compression

    ws.send("Hello!");
    ws.send("Compress this.", true);
    ws.send(new Uint8Array([1, 2, 3, 4]));
  • compress?: boolean
    ): number;

    Sends a binary message to the client.

    @param data

    The data to send.

    @param compress

    Whether to compress the data. Defaults to false. Ignored if the client does not support compression

    ws.sendBinary(new TextEncoder().encode("Hello!"));
    ws.sendBinary(new Uint8Array([1, 2, 3, 4]), true);
  • data: string,
    compress?: boolean
    ): number;

    Sends a text message to the client.

    @param data

    The data to send.

    @param compress

    Whether to compress the data. Defaults to false. Ignored if the client does not support compression

    ws.sendText("Hello!");
    ws.sendText("Compress this.", true);
  • topic: string
    ): boolean;

    Subscribes a client to the topic.

    @param topic

    The topic name.

    @returns

    true if the client is now subscribed, false if the socket is closed.

    ws.subscribe("chat");
  • terminate(): void;

    Closes the connection abruptly, without sending a Close frame. Messages already queued by send() and publish() are flushed first.

    To close the connection gracefully, use close().

  • topic: string
    ): boolean;

    Unsubscribes a client from the topic.

    @param topic

    The topic name.

    @returns

    true if the client was subscribed and is now unsubscribed, false if the socket is closed or the client was not subscribed to the topic.

    ws.unsubscribe("chat");