RPC - live()

Wrap a server function with live(). Import it in a component. Call it like a regular async function. It runs on the server over WebSocket.

Basic usage

// src/live/todos.js
import { live } from 'svelte-realtime/server';

export const addTodo = live(async (ctx, text) => {
  const todo = await db.todos.insert({ text, userId: ctx.user.id });
  ctx.publish('todos', 'created', todo);
  return todo;
});
<script>
  import { addTodo } from '$live/todos';

  async function add() {
    const todo = await addTodo('Buy milk');
  }
</script>

The ctx object

Every live() function receives ctx as its first argument. See The ctx Object for the full reference.

export const greet = live((ctx) => {
  return `Hello, ${ctx.user.name}`;
});

Multiple arguments

Arguments after ctx are passed from the client:

export const move = live(async (ctx, x, y) => {
  ctx.publish('positions', 'update', { key: ctx.user.id, x, y });
});
<script>
  import { move } from '$live/game';
  await move(100, 200);
</script>

Error handling

Throw LiveError on the server. Catch RpcError on the client.

// Server
import { live, LiveError } from 'svelte-realtime/server';

export const deleteItem = live(async (ctx, id) => {
  if (!ctx.user) throw new LiveError('UNAUTHORIZED', 'Login required');
  await db.items.delete(id);
});
<!-- Client -->
<script>
  import { deleteItem } from '$live/items';

  try {
    await deleteItem(itemId);
  } catch (err) {
    console.log(err.code);    // 'UNAUTHORIZED'
    console.log(err.message); // 'Login required'
  }
</script>

Schema validation

Use live.validated() with Zod or Valibot schemas:

import { z } from 'zod';
import { live } from 'svelte-realtime/server';

const CreateTodo = z.object({
  text: z.string().min(1).max(200),
  priority: z.enum(['low', 'medium', 'high']).optional()
});

export const addTodo = live.validated(CreateTodo, async (ctx, input) => {
  const todo = await db.todos.insert({ ...input, userId: ctx.user.id });
  ctx.publish('todos', 'created', todo);
  return todo;
});

Validation errors throw RpcError with code: 'VALIDATION' and an issues array.

Request deduplication

Identical calls in the same microtask are coalesced:

const [a, b] = await Promise.all([
  getUser(userId),
  getUser(userId) // reuses the first request
]);

Force a fresh request with .fresh():

const result = await getUser.fresh(userId);

Batching

Group multiple calls into a single WebSocket frame:

<script>
  import { batch } from 'svelte-realtime/client';
  import { createBoard, addColumn } from '$live/boards';

  const [board, column] = await batch(() => [
    createBoard('My Board'),
    addColumn('To Do')
  ]);
</script>

Pass { sequential: true } when order matters. Each call resolves independently - one failure doesn’t cancel the others. Max 50 calls per batch.


Under the hood, RPC calls are routed over the adapter’s WebSocket connection. See svelte-adapter-uws for the transport layer.

Was this page helpful?