Quick start
Sign a token on your backend, connect from your app, then publish and receive messages on a topic.
This page builds a minimal chat: your backend hands each signed-in user a token, and your app uses it to send and
receive messages on chat/lobby.
Before you start: set up the dashboard with a segment, a signing key and a topic
rule chat/> (class Durable, operations subscribe and publish), and install the SDK.
Store your keys on the backend
Copy the values from the segment's Connect tab into your backend's environment. Never put them in your app.
RTE_URL=https://rte.example.com
RTE_SEGMENT_ID=5faeecf0-db6e-41a3-9448-afe1b096a6d2
RTE_KEY_ID=backend-2026-10
RTE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
…
-----END PRIVATE KEY-----"Add a token endpoint to your backend
One endpoint, behind your normal login, signs a token for the signed-in user. Signing happens in your process; it makes no network call.
import express from 'express';
import { Folkways } from '@folkways/realtime/server';
const rte = new Folkways.Rest({
url: process.env.RTE_URL!,
segment: process.env.RTE_SEGMENT_ID!,
signingKey: { kid: process.env.RTE_KEY_ID!, privateKey: process.env.RTE_PRIVATE_KEY! },
});
const app = express();
// `requireLogin` is your own login middleware; it sets req.user.
app.post('/api/realtime-token', requireLogin, async (req, res) => {
const { token } = await rte.createToken({
identity: `user:${req.user.id}`,
capabilities: [{ topics: 'chat/>', ops: ['subscribe', 'publish'] }],
});
res.json({ token });
});
app.listen(3000);The token says who the user is (identity) and what they may do (capabilities). It lives 10 minutes; the SDK
asks for a new one before it expires.
Connect from your app
import { Folkways, isRealtimeError } from '@folkways/realtime';
const client = new Folkways.Realtime({
url: 'https://rte.example.com', // the node URL from the Connect tab
authUrl: '/api/realtime-token', // your endpoint from step 2
});
try {
const session = await client.connect();
console.log('connected as', session.identity); // 'user:42'
} catch (err) {
if (isRealtimeError(err)) console.error(err.codeName, err.message); // e.g. 'Forbidden'
throw err;
}Subscribe and publish
type ChatEvents = { message: { text: string } };
const lobby = client.topic<ChatEvents>('chat/lobby');
// Receive the last 20 messages, then every new one.
lobby.subscribe((msg) => {
console.log(`${msg.publisher}: ${msg.data.text}`);
}, { from: { last: 20 } });
// Send one. It resolves when Folkways has stored it.
const receipt = await lobby.publish('message', { text: 'Hello!' });
console.log('stored as', receipt.id);Open your app in two browsers signed in as different users: a message sent in one appears in the other.
What just happened
- Your app asked your backend for a token. Your backend signed it with the segment's private key.
- The SDK connected to the node over WebSocket and sent the token. The node checked the signature with the segment's public key.
subscribereplayed the last 20 messages ofchat/lobby, then delivered new ones as they arrived.publishsent amessageevent. Every subscriber, including your own app, received it.
If the network drops, the SDK reconnects, resubscribes from the last message it handled and sends anything you published while offline. You don't write code for that.