Real-time

WebSockets

Real-time communication with native Bun WebSockets, heartbeat, and rooms.

WebSockets

Bklar includes native WebSocket support using Bun.serve. Define WebSocket routes with the .ws() method and leverage heartbeat, rooms, and Pub/Sub.

Creating a WebSocket Route

app.ws("/chat", {
  open(ws) {
    console.log("Client connected");
    ws.send("Welcome!");
  },

  message(ws, msg) {
    console.log(`Received: ${msg}`);
  },

  close(ws) {
    console.log("Client disconnected");
  },
});

Heartbeat / Ping-Pong

Configure automatic ping intervals to detect dead connections:

app.ws("/realtime", {
  pingInterval: 30000,   // Send ping every 30s
  pongTimeout: 10000,    // Disconnect if no pong in 10s

  open(ws) {
    console.log("Connected with heartbeat");
  },
  message(ws, msg) {
    // handle message
  },
  close(ws, code, reason) {
    console.log(`Disconnected: ${code} ${reason}`);
  },
});

Rooms

Subscribe clients to rooms for targeted broadcasting:

app.ws("/notifications", {
  open(ws) {
    // Join user-specific room
    const userId = ws.data.ctx.state.jwt?.sub;
    app.join(ws, `user:${userId}`);
  },

  close(ws) {
    const userId = ws.data.ctx.state.jwt?.sub;
    app.leave(ws, `user:${userId}`);
  },
});

// HTTP endpoint sends to a room
app.post("/notify/:userId", (ctx) => {
  const { userId } = ctx.params;
  app.to(`user:${userId}`).sendText(JSON.stringify({
    type: "notification",
    message: "You have a new alert",
  }));

  return ctx.text("Sent");
});

Pub/Sub

Bklar exposes Bun's native Pub/Sub for broadcasting to topics. Use ws.subscribe() and app.broadcast():

app.ws("/alerts", {
  open(ws) {
    ws.subscribe("global-alerts");
  },
  message(ws, msg) {
    ws.publish("global-alerts", msg);
  },
});

// Broadcast from an HTTP handler
app.post("/alert", (ctx) => {
  app.broadcast("global-alerts", "System alert!");
  return ctx.text("Alert sent");
});

Accessing the Context

The ws.data.ctx property carries the full request context, including state from middleware:

import { jwt } from "@bklarjs/jwt";

app.use(jwt({ secret: "s3cret" }));

app.ws("/secure-chat", {
  open(ws) {
    const user = ws.data.ctx.state.jwt;
    console.log(`User ${user.sub} connected`);
    ws.send(`Hello, ${user.name}!`);
  },
});

Production Considerations

WebSocket connections count toward your server's open file descriptors. Use heartbeat to detect zombie connections and set idleTimeout in BklarOptions.websocket to prevent resource leaks.

On this page