RPCs

Some actions aren’t “write a value” — they’re “ask the server to do a thing”: play a card, fire a weapon, buy an item. Those are RPCs: typed functions the client calls and the server handles, with the argument and return types shared from one definition.

Define

// irtio/rpc.ts
import { defineRpc, number, string } from '@irtio/rpc';

export const rpc = defineRpc({
  playCard: { args: { card: string() }, returns: { ok: boolean() } },
  fire: { args: { angle: number(), power: number() } },
});

Handle on the server

The handler receives the current state, the typed args, and a context with the caller’s identity. It runs authoritatively — the client can’t skip it.

// irtio/room.ts
export default defineRoom(schema, {
  rpc: {
    playCard(state, { card }, ctx) {
      const hand = state.hands[ctx.clientId];
      if (!hand.includes(card)) return { ok: false }; // reject cheating
      hand.splice(hand.indexOf(card), 1);
      state.pile.push(card);
      return { ok: true };
    },
  },
});

Call from the client

const { ok } = await room.rpc.playCard({ card: '10♣' });
if (!ok) shake();

The resulting state changes stream back through your normal subscribe — you don’t apply the RPC’s effects yourself.

When to use an RPC vs. an owned write

Reach for…When
Owned writeyou own the value and just want it synced
RPCthe server must validate, arbitrate, or compute the result

Next steps

Placeholder content.