WebSockets

WebSocket provides bidirectional, persistent connections with room-based pub/sub. Radiant automatically configures WebSocket endpoints when enabled in your DSL.

Enabling WebSockets

In your .radiant file, you can enable WebSockets for a collection. The runtime will automatically broadcast CRUD events to connected clients.

collection todos {
  realtime: {
    ws: ["create", "update", "delete"] // true = all events
  }
  fields: {
    title: text
  }
}

When enabled, the runtime mounts GET /api/ws as the WebSocket upgrade endpoint.

Connecting

const ws = new WebSocket("ws://localhost:3000/api/ws");

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg);
};

On connect, the server sends:

{ "type": "connected", "id": "conn-uuid" }

Joining a Room

Join a collection's room to receive change events:

ws.send(JSON.stringify({ type: "join", room: "todos" }));

Response:

{ "type": "joined", "room": "todos" }

Receiving Change Events

When a CRUD operation occurs on a collection you've joined, you receive:

{
  "event": "todos:created",
  "data": { "id": "abc-123", "title": "Buy groceries", "completed": false }
}

The event name follows the pattern <collection>:<action>:

  • todos:created
  • todos:updated
  • todos:deleted

Leaving a Room

ws.send(JSON.stringify({ type: "leave", room: "todos" }));

Broadcasting to a Room

From your TypeScript code, broadcast a custom message to all connections in a room:

import { RadiantWebsocket } from "@codesordinatestudio/radiant-bun";

RadiantWebsocket.broadcastToRoom("todos", {
  type: "notification",
  message: "System maintenance in 10 minutes",
});

WebSocket Manager API

// Send to a specific connection
RadiantWebsocket.sendTo(connectionId, { type: "ping" });

// Broadcast to all connections
RadiantWebsocket.broadcastAll({ type: "announcement" });

// Broadcast with exclusions
RadiantWebsocket.broadcastToRoom("todos", payload, { exclude: [senderId] });

// Get room members
const members = RadiantWebsocket.getRoomMembers("todos");

// Stats
const stats = RadiantWebsocket.getStats();
// { connections: 15, rooms: { todos: 12, posts: 3 }, heartbeatActive: true }

Ping/Pong Heartbeat

The WebSocket manager has a built-in heartbeat to detect dead connections:

// Client responds to server pings
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "ping") {
    ws.send(JSON.stringify({ type: "pong" }));
  }
};