Publishing
Send events to a topic, get a receipt once they are stored, and rely on retries and the offline queue.
const room = client.topic('chat/lobby');
const receipt = await room.publish('message', { text: 'Hello!' });
// receipt: { topic, delivery: 'stream', id, uid, cursor, level: 'accepted', duplicate: false, serverTime }topic.publish(event, data, options?) sends one event:
eventis the event's name, such asmessage(a non-empty string, at most 64 bytes);datais the payload: any JSON value, a string or bytes (see Payloads);optionsare optional; the most common isdelivery.
For stream and kv events, publish() resolves with a receipt once the
node has stored the event, and rejects with a RealtimeError if it can't. Signals resolve with
nothing.
Options
await room.publish('message', { text: 'Hello!' }, {
headers: { lang: 'en' }, // passed to subscribers unchanged
idempotencyKey: crypto.randomUUID(),
});| Option | Default | Meaning |
|---|---|---|
delivery | 'stream' | 'stream', 'kv' or 'signal'. Must match the topic's rule |
key | your identity | kv only: which key to set |
ttlMs | none | kv only: how long the value lives |
remove | false | kv only: delete the key instead of setting it |
headers | none | Up to 16 string headers, passed to subscribers |
contentType | from data | Label the payload: 'json', 'text', 'bytes', 'flatbuffers', 'protobuf', 'msgpack', 'cbor' or a number from 1024 |
idempotencyKey | generated | 32 hex digits or a UUID. Reuse it to retry the same publish |
clientTime | Date.now() | Your clock for the event, in Unix ms |
signal | none | An AbortSignal to stop waiting for the receipt (the event may still be published) |
Delivery guarantees
- Stored at most once. Every stream and kv publish carries an idempotency key, and the SDK reuses it on every
retry. If the node already stored the event, it answers with the original receipt and
duplicate: true, which is still a success. - Automatic retries. When the node is busy or rate-limits you, the SDK waits as long as the node asks and
retries, up to 3 times (
publishRetries). A publish gives up after 30 seconds in total (publishTimeoutMs) withTimeout. - Offline queue. Publishes made while reconnecting are queued, up to 1,000 (
offlineQueue.maxSize), and sent in order once connected. Events that were in flight when the connection dropped are sent again with the same key. - Offline for long. After 2 minutes without a connection (
offlineAfterMs), new publishes fail withNotConnectedinstead of waiting. With a persistent store, they are kept in an outbox and sent when the connection returns, even after a page reload. - Checked locally first. A bad topic name, an oversized payload or too many headers fail at once with
InvalidTopic,PayloadTooLargeorInvalidArgument, without a round trip.
Retrying the same event yourself
If your code may publish the same thing twice, for example after a page reload, give it a stable key:
await room.publish('message', { text: 'Order #123 confirmed' }, { idempotencyKey: orderUuid });The node remembers keys for 10 minutes. The same key with different content fails with IdempotencyConflict.
Your own sends in the UI
For stream events, each send also appears at once in topic.messages, with status: 'pending', then 'sent' when
the receipt arrives, or 'failed' with a retry() method. See Using it with React.
Several topics at once
const rooms = client.topic(['chat/a', 'chat/b']);
const receipts = await rooms.publish('message', { text: 'Maintenance at 18:00' }); // one receipt per topicShortcut
Folkways.connect(options) creates the client and connects in one call, which is handy in scripts:
const client = await Folkways.connect({ url: 'https://rte.example.com', token });
await client.topic('jobs/new').publish('job', { id: 42 });
await client.close();