> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSockets

> Real-time bidirectional communication with WebSockets in Bun

Bun provides fast, built-in WebSocket support via `Bun.serve()`. WebSocket connections are upgraded from HTTP requests and support pub/sub messaging, backpressure handling, and compression.

## Basic WebSocket Server

```ts theme={null}
Bun.serve({
  websocket: {
    open(ws) {
      console.log("Client connected");
    },
    message(ws, message) {
      console.log("Received:", message);
      ws.send(message); // Echo back
    },
    close(ws, code, reason) {
      console.log("Client disconnected", code, reason);
    },
  },
  fetch(req, server) {
    const url = new URL(req.url);
    if (url.pathname === "/chat") {
      const upgraded = server.upgrade(req);
      if (upgraded) return; // Don't return a Response
    }
    return new Response("Hello HTTP");
  },
});
```

## WebSocket Handler Configuration

### Message Handler

```ts theme={null}
websocket: {
  message(ws, message) {
    // message type depends on binaryType
    if (typeof message === "string") {
      console.log("Text message:", message);
    } else {
      console.log("Binary message:", message);
    }
  },
}
```

### Binary Type

```ts theme={null}
websocket: {
  open(ws) {
    // Set how binary data is returned
    ws.binaryType = "nodebuffer"; // Default
    // ws.binaryType = "arraybuffer";
    // ws.binaryType = "uint8array";
  },
  message(ws, message) {
    // Type of message depends on binaryType:
    // "nodebuffer" => Buffer
    // "arraybuffer" => ArrayBuffer  
    // "uint8array" => Uint8Array
  },
}
```

## Upgrading HTTP Connections

### Basic Upgrade

```ts theme={null}
fetch(req, server) {
  const success = server.upgrade(req);
  if (success) {
    // WebSocket upgrade successful, don't return a Response
    return;
  }
  return new Response("Upgrade failed", { status: 400 });
}
```

### Upgrade with Custom Data

```ts theme={null}
interface WebSocketData {
  userId: string;
  username: string;
}

Bun.serve<WebSocketData>({
  websocket: {
    message(ws, message) {
      console.log(`${ws.data.username} says: ${message}`);
    },
  },
  fetch(req, server) {
    const url = new URL(req.url);
    const username = url.searchParams.get("name") || "Anonymous";
    
    const upgraded = server.upgrade(req, {
      data: {
        userId: crypto.randomUUID(),
        username,
      },
    });
    
    if (upgraded) return;
    return new Response("Upgrade failed", { status: 400 });
  },
});
```

### Upgrade with Custom Headers

```ts theme={null}
server.upgrade(req, {
  headers: {
    "Set-Cookie": "session=abc123",
  },
});
```

## Sending Messages

### Send Text or Binary

```ts theme={null}
// Send text
ws.send("Hello");

// Send binary
ws.send(new Uint8Array([1, 2, 3, 4]));

// With compression
ws.send("Compress this message", true);
```

### Explicit Text/Binary

```ts theme={null}
// Always send as text
const status = ws.sendText("Hello", compress);

// Always send as binary
const status = ws.sendBinary(new Uint8Array([1, 2, 3]), compress);
```

### Send Status

All send methods return a status number:

```ts theme={null}
const status = ws.send("Hello");

if (status === 0) {
  console.log("Message was dropped");
} else if (status === -1) {
  console.log("Backpressure applied");
} else {
  console.log(`Sent ${status} bytes`);
}
```

## Pub/Sub Messaging

### Subscribe to Topics

```ts theme={null}
websocket: {
  open(ws) {
    // Subscribe to topics
    ws.subscribe("chat");
    ws.subscribe("notifications");
  },
  message(ws, message) {
    // Publish to all subscribers in a topic
    ws.publish("chat", message);
  },
}
```

### Check Subscriptions

```ts theme={null}
if (ws.isSubscribed("chat")) {
  console.log("Subscribed to chat");
}

// Get all subscriptions
const topics = ws.subscriptions;
console.log("Subscribed to:", topics); // ["chat", "notifications"]
```

### Unsubscribe

```ts theme={null}
ws.unsubscribe("chat");
```

### Publish from HTTP Handler

```ts theme={null}
Bun.serve({
  websocket: {
    open(ws) {
      ws.subscribe("global");
    },
  },
  fetch(req, server) {
    if (req.method === "POST") {
      const data = await req.text();
      // Publish to all WebSocket subscribers
      server.publish("global", data);
      return new Response("Published");
    }
    return new Response("Send POST to publish");
  },
});
```

### Subscriber Count

```ts theme={null}
const count = server.subscriberCount("chat");
console.log(`${count} clients in chat`);
```

## Ping/Pong

```ts theme={null}
websocket: {
  open(ws) {
    // Manual ping
    ws.ping("optional data");
  },
  ping(ws, data) {
    console.log("Received ping", data);
  },
  pong(ws, data) {
    console.log("Received pong", data);
  },
}
```

### Automatic Pings

```ts theme={null}
websocket: {
  sendPings: true, // Default: true
  // Server automatically sends pings
}
```

## Connection Management

### Closing Connections

```ts theme={null}
// Graceful close
ws.close(1000, "Normal closure");

// Abrupt termination
ws.terminate();
```

### Close Codes

* `1000` - Normal closure (default)
* `1009` - Message too big
* `1011` - Server error
* `1012` - Server restarting
* `1013` - Try again later
* `4000-4999` - Application-specific codes

### Ready State

```ts theme={null}
const state = ws.readyState;
// 0 = CONNECTING
// 1 = OPEN
// 2 = CLOSING
// 3 = CLOSED
```

### Remote Address

```ts theme={null}
console.log(ws.remoteAddress); // "127.0.0.1"
```

## Backpressure and Buffering

### Check Buffered Amount

```ts theme={null}
const buffered = ws.getBufferedAmount();
if (buffered > 1024 * 1024) {
  console.log("Too much data buffered");
}
```

### Drain Handler

Called when backpressure is relieved:

```ts theme={null}
websocket: {
  drain(ws) {
    console.log("Ready to send more data");
    // Send queued messages
  },
}
```

### Backpressure Limits

```ts theme={null}
websocket: {
  backpressureLimit: 1024 * 1024 * 16, // 16 MB (default)
  closeOnBackpressureLimit: false, // Don't auto-close
}
```

## Corking

Batch multiple send operations:

```ts theme={null}
ws.cork((ws) => {
  ws.send("Message 1");
  ws.send("Message 2");
  ws.send("Message 3");
  // All sent together
});
```

## Configuration Options

```ts theme={null}
websocket: {
  // Size limits
  maxPayloadLength: 16 * 1024 * 1024, // 16 MB (default)
  backpressureLimit: 16 * 1024 * 1024, // 16 MB (default)
  closeOnBackpressureLimit: false,
  
  // Timeouts
  idleTimeout: 120, // seconds (default)
  
  // Pub/sub
  publishToSelf: false, // Don't echo publishes to sender
  
  // Pings
  sendPings: true, // Auto-send pings
  
  // Compression
  perMessageDeflate: {
    compress: true,
    decompress: true,
  },
}
```

## Compression

```ts theme={null}
websocket: {
  perMessageDeflate: {
    compress: "3KB", // Compress messages > 3KB
    decompress: true,
  },
  // Or use boolean:
  // perMessageDeflate: true,
}
```

Compression levels: `"disable"`, `"shared"`, `"dedicated"`, `"3KB"`, `"4KB"`, `"8KB"`, `"16KB"`, `"32KB"`, `"64KB"`, `"128KB"`, `"256KB"`

## Type Signatures

```ts theme={null}
interface ServerWebSocket<T = undefined> {
  send(data: string | BufferSource, compress?: boolean): number;
  sendText(data: string, compress?: boolean): number;
  sendBinary(data: BufferSource, compress?: boolean): number;
  
  close(code?: number, reason?: string): void;
  terminate(): void;
  
  ping(data?: string | BufferSource): number;
  pong(data?: string | BufferSource): number;
  
  publish(topic: string, data: string | BufferSource, compress?: boolean): number;
  publishText(topic: string, data: string, compress?: boolean): number;
  publishBinary(topic: string, data: BufferSource, compress?: boolean): number;
  
  subscribe(topic: string): void;
  unsubscribe(topic: string): void;
  isSubscribed(topic: string): boolean;
  
  cork<T>(callback: (ws: ServerWebSocket) => T): T;
  getBufferedAmount(): number;
  
  readonly subscriptions: string[];
  readonly remoteAddress: string;
  readonly readyState: 0 | 1 | 2 | 3;
  binaryType?: "nodebuffer" | "arraybuffer" | "uint8array";
  data: T;
}
```
