Durable Streams

Durable streams persist change events so clients can replay them after a disconnection. Unlike WS and SSE which are ephemeral, durable streams store events in a log (in-memory for development, Redis/ElectricSQL for production).

Enabling Durable Streams

In your .radiant file, you can enable Durable Streams for a collection. The runtime will automatically broadcast and store CRUD events for connected clients to replay.

collection todos {
  realtime: {
    durableStream: true
  }
  fields: {
    title: text
  }
}

When enabled, the runtime mounts GET /api/<slug>/stream as the durable stream read endpoint.

Reading Events

# Get all events for a collection
GET /api/todos/stream

# Get events after a specific event ID (for replay after reconnect)
GET /api/todos/stream?lastEventId=1234567890-abcde

Response:

[
  {
    "id": "1234567890-abcde",
    "collection": "todos",
    "action": "created",
    "data": { "id": "abc-123", "title": "Buy groceries" },
    "timestamp": 1234567890000
  }
]

Default: In-Memory Store

By default, Radiant uses MemoryStreamStore — a ring buffer (max 1000 events per collection) that lives in the process memory. This is development-only: events are lost on restart and don't work across multiple servers.

Production: Durable Streams Plugin

For production, use the durable streams plugin which persists events to an external store:

bun add @codesordinatestudio/radiant-plugin-durable-streams
import { createRadiant } from "../radiant/gen";
import { sqlite } from "@codesordinatestudio/radiant-plugin-sqlite";
import { durableStreams } from "@codesordinatestudio/radiant-plugin-durable-streams";

export const app = createRadiant({
  adapter: sqlite({ url: process.env.DATABASE_URL! }),
  streamStore: durableStreams({
    url: process.env.DURABLE_STREAMS_URL!,
  }),
});

Reading Events Programmatically

// Read all events
const events = await app.streamStore.read("todos");

// Read events after a cursor
const newEvents = await app.streamStore.read("todos", "last-event-id");