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.
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
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
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
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
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
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 });
},
});
server.upgrade(req, {
headers: {
"Set-Cookie": "session=abc123",
},
});
Sending Messages
Send Text or Binary
// 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
// 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:
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
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
if (ws.isSubscribed("chat")) {
console.log("Subscribed to chat");
}
// Get all subscriptions
const topics = ws.subscriptions;
console.log("Subscribed to:", topics); // ["chat", "notifications"]
Unsubscribe
Publish from HTTP Handler
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
const count = server.subscriberCount("chat");
console.log(`${count} clients in chat`);
Ping/Pong
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
websocket: {
sendPings: true, // Default: true
// Server automatically sends pings
}
Connection Management
Closing Connections
// 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
const state = ws.readyState;
// 0 = CONNECTING
// 1 = OPEN
// 2 = CLOSING
// 3 = CLOSED
Remote Address
console.log(ws.remoteAddress); // "127.0.0.1"
Backpressure and Buffering
Check Buffered Amount
const buffered = ws.getBufferedAmount();
if (buffered > 1024 * 1024) {
console.log("Too much data buffered");
}
Drain Handler
Called when backpressure is relieved:
websocket: {
drain(ws) {
console.log("Ready to send more data");
// Send queued messages
},
}
Backpressure Limits
websocket: {
backpressureLimit: 1024 * 1024 * 16, // 16 MB (default)
closeOnBackpressureLimit: false, // Don't auto-close
}
Corking
Batch multiple send operations:
ws.cork((ws) => {
ws.send("Message 1");
ws.send("Message 2");
ws.send("Message 3");
// All sent together
});
Configuration Options
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
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
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;
}