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");