Platform API

Available in server hooks, load functions, form actions, API routes, and WebSocket hooks (hooks.ws).

Access it via event.platform in SvelteKit handlers or destructured from the second argument in WebSocket hooks.


platform.publish(topic, event, data, options?)

Send a message to all WebSocket clients subscribed to a topic.

Topic and event names are validated before being written into the JSON envelope - quotes, backslashes, and control characters will throw. This prevents JSON injection when names are built from dynamic values like user IDs. The validation is a single-pass char scan and adds no measurable overhead.

// src/routes/todos/+page.server.js
export const actions = {
  create: async ({ request, platform }) => {
    const formData = await request.formData();
    const todo = await db.createTodo(formData.get('text'));

    // Every client subscribed to 'todos' receives this
    platform.publish('todos', 'created', todo);

    return { success: true };
  }
};

Cluster mode relay

In cluster mode, the message is automatically relayed to all other workers. Pass { relay: false } to skip the relay when the message originates from an external pub/sub source (Redis, Postgres LISTEN/NOTIFY, etc.) that already delivers to every process:

// Redis subscriber running on every worker -- relay would cause duplicates
sub.on('message', (channel, payload) => {
  platform.publish(channel, 'update', JSON.parse(payload), { relay: false });
});

platform.send(ws, topic, event, data)

Send a message to a single WebSocket connection. Wraps in the same { topic, event, data } envelope as publish().

This is useful when you store WebSocket references (e.g. in a Map) and need to message specific connections from SvelteKit handlers:

// src/hooks.ws.js - store connections by user ID
const userSockets = new Map();

export function open(ws, { platform }) {
  const { userId } = ws.getUserData();
  userSockets.set(userId, ws);
}

export function close(ws, { platform }) {
  const { userId } = ws.getUserData();
  userSockets.delete(userId);
}

// Export the map so SvelteKit handlers can access it
export { userSockets };
// src/routes/api/dm/+server.js - send to a specific user
import { userSockets } from '../../hooks.ws.js';

export async function POST({ request, platform }) {
  const { targetUserId, message } = await request.json();
  const ws = userSockets.get(targetUserId);
  if (ws) {
    platform.send(ws, 'dm', 'new-message', { message });
  }
  return new Response('OK');
}

You can also reply directly from inside hooks.ws.js using platform.send() or ws.send() with the envelope format:

// src/hooks.ws.js
export function message(ws, { data, platform }) {
  const msg = JSON.parse(Buffer.from(data).toString());
  // Using platform.send (recommended):
  platform.send(ws, 'echo', 'reply', { got: msg });
  // Or using ws.send with manual envelope:
  ws.send(JSON.stringify({ topic: 'echo', event: 'reply', data: { got: msg } }));
}

platform.sendTo(filter, topic, event, data)

Send a message to all connections whose userData matches a filter function. Returns the number of connections the message was sent to.

This is simpler than manually maintaining a Map of connections - no hooks.ws.js needed:

// src/routes/api/dm/+server.js - send to a specific user
export async function POST({ request, platform }) {
  const { targetUserId, message } = await request.json();
  const count = platform.sendTo(
    (userData) => userData.userId === targetUserId,
    'dm', 'new-message', { message }
  );
  return new Response(count > 0 ? 'Sent' : 'User offline');
}
// Send to all admins
platform.sendTo(
  (userData) => userData.role === 'admin',
  'alerts', 'warning', { message: 'Server load high' }
);

Performance: sendTo iterates every open connection and runs your filter function against each one. It’s fine for low-frequency operations like sending a DM or notifying admins, but don’t use it in a hot loop. If you’re broadcasting to a known group of users, subscribe them to a shared topic and use platform.publish() instead - topic-based pub/sub is handled natively by uWS in C++ and doesn’t touch the JS event loop.


platform.connections

Number of active WebSocket connections:

// src/routes/api/stats/+server.js
import { json } from '@sveltejs/kit';

export async function GET({ platform }) {
  return json({ online: platform.connections });
}

platform.subscribers(topic)

Number of clients subscribed to a specific topic:

export async function GET({ platform, params }) {
  return json({
    viewers: platform.subscribers(`page:${params.id}`)
  });
}

platform.topic(name)

Scoped helper that reduces repetition when publishing multiple events to the same topic.

CRUD methods

Calling .created(), .updated(), or .deleted() is shorthand for platform.publish(name, 'created', data), etc.:

// src/routes/todos/+page.server.js
export const actions = {
  create: async ({ request, platform }) => {
    const todos = platform.topic('todos');
    const todo = await db.create(await request.formData());
    todos.created(todo);  // shorthand for platform.publish('todos', 'created', todo)
  },

  update: async ({ request, platform }) => {
    const todos = platform.topic('todos');
    const todo = await db.update(await request.formData());
    todos.updated(todo);
  },

  delete: async ({ request, platform }) => {
    const todos = platform.topic('todos');
    const id = (await request.formData()).get('id');
    await db.delete(id);
    todos.deleted({ id });
  }
};

Counter methods

The topic helper also has counter methods for numeric state:

const online = platform.topic('online-users');
online.set(42);         // -> { event: 'set', data: 42 }
online.increment();     // -> { event: 'increment', data: 1 }
online.increment(5);    // -> { event: 'increment', data: 5 }
online.decrement();     // -> { event: 'decrement', data: 1 }
MethodEvent publishedData
.created(data)createdThe data argument
.updated(data)updatedThe data argument
.deleted(data)deletedThe data argument
.set(value)setThe value
.increment(n?)incrementn (default 1)
.decrement(n?)decrementn (default 1)

platform.batch(messages)

Publish multiple messages in a single call. Useful when an action updates several topics at once:

platform.batch([
  { topic: 'todos', event: 'created', data: todo },
  { topic: `user:${userId}`, event: 'activity', data: { action: 'create' } },
  { topic: 'stats', event: 'increment', data: { key: 'todos_created' } }
]);

Each entry is published with platform.publish(). Cross-worker relay is batched automatically, so this is more efficient than three separate publish() calls from a relay overhead perspective.


Summary

MethodDescription
publish(topic, event, data, options?)Broadcast to all subscribers of a topic
send(ws, topic, event, data)Send to a single connection
sendTo(filter, topic, event, data)Send to connections matching a filter
connectionsNumber of active WebSocket connections
subscribers(topic)Number of subscribers on a topic
topic(name)Scoped helper with .created(), .updated(), .deleted(), .set(), .increment(), .decrement()
batch(messages)Publish multiple messages in one call

Was this page helpful?