The JavaScript SDK is in pre-release and not on npm yet. Install it from source.
FolkwaysDocs

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:

  • event is the event's name, such as message (a non-empty string, at most 64 bytes);
  • data is the payload: any JSON value, a string or bytes (see Payloads);
  • options are optional; the most common is delivery.

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(),
});
OptionDefaultMeaning
delivery'stream''stream', 'kv' or 'signal'. Must match the topic's rule
keyyour identitykv only: which key to set
ttlMsnonekv only: how long the value lives
removefalsekv only: delete the key instead of setting it
headersnoneUp to 16 string headers, passed to subscribers
contentTypefrom dataLabel the payload: 'json', 'text', 'bytes', 'flatbuffers', 'protobuf', 'msgpack', 'cbor' or a number from 1024
idempotencyKeygenerated32 hex digits or a UUID. Reuse it to retry the same publish
clientTimeDate.now()Your clock for the event, in Unix ms
signalnoneAn 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) with Timeout.
  • 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 with NotConnected instead 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, PayloadTooLarge or InvalidArgument, 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 topic

Shortcut

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

On this page