Quick Start Guide
This guide walks you through using the Unbroken Protocol. You’ll create a stream, write data, and read it back using different modes.
Prerequisites
- A running Unbroken Protocol server (default port: 9999)
curlfor command-line examples- A modern browser for SSE examples
Starting the Server
# Using the reference implementationbun run start:dev
# Server starts on http://localhost:9999Step 1: Create a Stream
Create a new JSON stream:
curl -X PUT http://localhost:9999/events \ -H "Content-Type: application/json" \ -vResponse:
HTTP/1.1 201 CreatedLocation: /eventsContent-Type: application/jsonStream-Next-Offset: 0000000000000000_0000000000000000The stream is created and ready for data.
Step 2: Append Data
Add an event to the stream:
curl -X POST http://localhost:9999/events \ -H "Content-Type: application/json" \ -d '{"event": "user_signup", "user_id": 42}' \ -vResponse:
HTTP/1.1 204 No ContentStream-Next-Offset: 0000000000000000_0000000000000039The data is now durably stored. Add more events:
# Batch append (array is flattened)curl -X POST http://localhost:9999/events \ -H "Content-Type: application/json" \ -d '[{"event": "page_view", "page": "/home"}, {"event": "click", "button": "signup"}]'Step 3: Read Data (Catch-Up)
Read all data from the beginning:
curl "http://localhost:9999/events?offset=-1"Response:
[ { "event": "user_signup", "user_id": 42 }, { "event": "page_view", "page": "/home" }, { "event": "click", "button": "signup" }]Headers include:
Stream-Next-Offset: 0000000000000000_0000000000000108Stream-Up-To-Date: trueStep 4: Read Data (Long-Poll)
Wait for new data using long-polling:
# In terminal 1: Wait for new datacurl "http://localhost:9999/events?offset=0000000000000000_0000000000000108&live=long-poll"
# In terminal 2: Append new datacurl -X POST http://localhost:9999/events \ -H "Content-Type: application/json" \ -d '{"event": "purchase", "amount": 99.99}'Terminal 1 immediately receives the new data:
[{ "event": "purchase", "amount": 99.99 }]Step 5: Read Data (SSE)
For real-time streaming in browsers, use Server-Sent Events:
// Browser JavaScriptconst offset = "-1" // Start from beginningconst eventSource = new EventSource(`/events?offset=${offset}&live=sse`)
let currentOffset = offset
eventSource.addEventListener("data", (event) => { const data = JSON.parse(event.data) console.log("Received:", data)})
eventSource.addEventListener("control", (event) => { const control = JSON.parse(event.data) currentOffset = control.streamNextOffset console.log("Offset:", currentOffset)})
eventSource.onerror = () => { console.log("Connection lost, reconnecting...") // Reconnect with currentOffset}Or using curl:
curl "http://localhost:9999/events?offset=-1&live=sse"Output:
event: datadata: [{"event":"user_signup","user_id":42}]
event: controldata: {"streamNextOffset":"0000000000000000_0000000000000039"}
event: datadata: [{"event":"page_view","page":"/home"},{"event":"click","button":"signup"}]
event: controldata: {"streamNextOffset":"0000000000000000_0000000000000108"}Step 6: Resumable Reading
Persist the offset to resume after disconnection:
// Save offset to localStoragefunction saveOffset(offset) { localStorage.setItem("events_offset", offset)}
function loadOffset() { return localStorage.getItem("events_offset") || "-1"}
// Use saved offset when connectingconst offset = loadOffset()const eventSource = new EventSource(`/events?offset=${offset}&live=sse`)
eventSource.addEventListener("control", (event) => { const { streamNextOffset } = JSON.parse(event.data) saveOffset(streamNextOffset)})Step 7: Stream Metadata
Check stream existence and get current offset:
curl -I http://localhost:9999/eventsResponse:
HTTP/1.1 200 OKContent-Type: application/jsonStream-Next-Offset: 0000000000000000_0000000000000142Step 8: Delete Stream
Remove the stream when done:
curl -X DELETE http://localhost:9999/eventsResponse:
HTTP/1.1 204 No ContentComplete Example: Chat Application
Here’s a complete example using the protocol for a chat application:
Server-Side (Creating Conversations)
// Create a conversation streamawait fetch("/conversations/room-123", { method: "PUT", headers: { "Content-Type": "application/json" },})Sending Messages
async function sendMessage(roomId, message) { await fetch(`/conversations/${roomId}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ type: "message", user: currentUser, text: message, timestamp: Date.now(), }), })}Receiving Messages
function connectToRoom(roomId, onMessage) { let offset = localStorage.getItem(`room_${roomId}_offset`) || "-1"
const eventSource = new EventSource( `/conversations/${roomId}?offset=${offset}&live=sse` )
eventSource.addEventListener("data", (event) => { const messages = JSON.parse(event.data) messages.forEach(onMessage) })
eventSource.addEventListener("control", (event) => { const { streamNextOffset } = JSON.parse(event.data) localStorage.setItem(`room_${roomId}_offset`, streamNextOffset) })
return () => eventSource.close()}
// Usageconst disconnect = connectToRoom("room-123", (msg) => { console.log(`${msg.user}: ${msg.text}`)})Using the TypeScript Client
For TypeScript/JavaScript applications, use the official client:
npm install @unbroken-protocol/clientimport { UnbrokenStream } from "@unbroken-protocol/client"
// Create a stream handleconst stream = new UnbrokenStream("http://localhost:9999/events", { contentType: "application/json",})
// Write dataawait stream.append({ event: "click", x: 100, y: 200 })
// Read with subscriptionconst response = await stream.read({ offset: "-1" })
for await (const message of response.messages()) { console.log("Message:", message.data)}Next Steps
- Core Concepts - Deeper understanding of the protocol
- Reading Operations - All read modes in detail
- Writing Operations - Advanced write patterns
- Offsets - Offset semantics and persistence