The ctx Object
Every live() function receives ctx as its first argument. It contains the connection, user data, and publishing utilities.
Properties
| Field | Type | Description |
|---|---|---|
ctx.user | object | Whatever your upgrade() returned |
ctx.ws | WebSocket | The raw WebSocket connection |
ctx.platform | object | The adapter platform API |
ctx.cursor | string \| null | Cursor from a loadMore() call |
Methods
ctx.publish(topic, event, data)
Send an event to all clients subscribed to topic.
ctx.publish('messages', 'created', { id: 1, text: 'hello' }); ctx.throttle(topic, event, data, ms)
Publish at most once per ms milliseconds. First call goes immediately, then stores subsequent calls and sends the last value when the interval expires.
export const updatePosition = live(async (ctx, x, y) => {
ctx.throttle('cursors', 'update', { key: ctx.user.id, x, y }, 50);
}); ctx.debounce(topic, event, data, ms)
Wait for ms milliseconds of silence before publishing. Each call resets the timer.
export const saveSearch = live(async (ctx, query) => {
ctx.debounce('search:' + ctx.user.id, 'set', { query }, 300);
}); ctx.signal(userId, event, data)
Point-to-point message to a specific user. Only that user receives it.
export const nudge = live(async (ctx, targetUserId) => {
ctx.signal(targetUserId, 'nudge', { from: ctx.user.name });
}); ctx.batch(messages)
Publish multiple messages in a single call:
export const reset = live(async (ctx, boardId) => {
ctx.batch([
{ topic: `board:${boardId}`, event: 'set', data: [] },
{ topic: `board:${boardId}:presence`, event: 'set', data: [] }
]);
}); Rate limiting
Per-function rate limiting
Use live.rateLimit() to apply a sliding window rate limiter to a single function. If a client exceeds the limit, the call throws a rate-limit error.
export const sendMessage = live.rateLimit({ points: 5, window: 10000 }, async (ctx, text) => {
const msg = await db.messages.insert({ userId: ctx.user.id, text });
ctx.publish('messages', 'created', msg);
return msg;
}); The config object takes two fields:
| Option | Description |
|---|---|
points | Maximum number of calls allowed within the window |
window | Sliding window size in milliseconds |
In this example, each client can call sendMessage at most 5 times per 10 seconds. The rate limiter tracks calls per WebSocket connection → different users have independent limits.
Webhooks
Bridge external HTTP webhooks into your pub/sub topics using live.webhook(). This turns incoming HTTP requests (from Stripe, GitHub, etc.) into stream events.
// src/live/integrations.js
import { live } from 'svelte-realtime/server';
export const stripeEvents = live.webhook('payments', {
verify({ body, headers }) {
return stripe.webhooks.constructEvent(body, headers['stripe-signature'], webhookSecret);
},
transform(event) {
if (event.type === 'payment_intent.succeeded') {
return { event: 'created', data: event.data.object };
}
return null; // ignore other event types
}
}); Use the handler in a SvelteKit endpoint:
// src/routes/api/stripe/+server.js
import { stripeEvents } from '$live/integrations';
export async function POST({ request, platform }) {
const body = await request.text();
const headers = Object.fromEntries(request.headers);
const result = await stripeEvents.handle({ body, headers, platform });
return new Response(result.body, { status: result.status });
} The flow is: HTTP request → verify() (authenticate the webhook) → transform() (map to a stream event) → ctx.publish() under the hood. If transform returns null, the event is silently ignored.
Signals
Point-to-point ephemeral messaging. Send a signal to a specific user without broadcasting to a topic. Useful for WebRTC signaling, notifications, direct nudges, and any case where only one user should receive the message.
Sending signals (server)
Use ctx.signal(userId, event, data) from any live() function:
// Server: send a signal
const handler = live(async (ctx, targetUserId, offer) => {
ctx.signal(targetUserId, 'call:offer', offer);
}); Receiving signals (client)
Use onSignal from svelte-realtime/client to listen for incoming signals:
// Client: receive signals
import { onSignal } from 'svelte-realtime/client';
const unsub = onSignal(currentUser.id, (event, data) => {
if (event === 'call:offer') showIncomingCall(data);
}); Enabling signal delivery
Enable signal delivery in your open hook:
import { enableSignals } from 'svelte-realtime/server';
export function open(ws) { enableSignals(ws); } Signals are ephemeral - they are not stored or replayed. If the target user is offline, the signal is lost.
Under the hood, ctx.publish and ctx.platform delegate to the adapter’s platform API. See svelte-adapter-uws for the full platform reference.
Was this page helpful?