Back to Rivet

Connections

website/src/content/docs/actors/connections.mdx

2.2.111.2 KB
Original Source

For documentation on connecting to actors from clients, see the Clients documentation.

Parameters

When clients connect to an actor, they can pass connection parameters that are handled during the connection process. Use params for static values, or getParams when you need fresh connection data each time a connection opens.

For example:

<CodeGroup>
typescript
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

interface ConnParams {
  authToken: string;
}

interface ConnState {
  userId: string;
  role: string;
}

const gameRoom = actor({
  state: {},
  createConnState: (c, params: ConnParams): ConnState => {
    return { userId: "user-123", role: "player" };
  },
  actions: {}
});

const registry = setup({ use: { gameRoom } });
const client = createClient<typeof registry>("http://localhost:6420");

async function getAuthToken(): Promise<string> {
  return "supersekure";
}

const gameRoomHandle = client.gameRoom.getOrCreate(["room-123"], {
  getParams: async () => ({
    authToken: await getAuthToken(),
  })
});
typescript
import { actor } from "rivetkit";

interface ConnParams {
  authToken: string;
}

interface ConnState {
  userId: string;
  role: string;
}

// Example validation functions
function validateToken(token: string): boolean {
  return token.length > 0;
}

function getUserIdFromToken(token: string): string {
  return "user-" + token.slice(0, 8);
}

const gameRoom = actor({
  state: {},

  // Handle connection setup
  createConnState: (c, params: ConnParams): ConnState => {
    // Validate authentication token
    const authToken = params.authToken;

    if (!authToken || !validateToken(authToken)) {
      throw new Error("Invalid auth token");
    }

    // Create connection state
    return { userId: getUserIdFromToken(authToken), role: "player" };
  },

  actions: {}
});
</CodeGroup>

Connection State

There are two ways to define an actor's connection state:

<Tabs> <Tab title="connState"> Define connection state as a constant value:
	```typescript
	import { actor } from "rivetkit";

	const chatRoom = actor({
	  state: { messages: [] },

	  // Define default connection state as a constant
	  connState: {
	    role: "guest",
	    joinedAt: 0
	  },

	  onConnect: (c) => {
	    // Update join timestamp when a client connects
	    c.conn.state.joinedAt = Date.now();
	  },

	  actions: {
	    // ...
	  }
	});
	```

	This value will be cloned for every new connection using `structuredClone`.
</Tab>

<Tab title="createConnState">
	Create connection state dynamically with a function called for each connection:

	```typescript
	import { actor } from "rivetkit";

	interface ConnState {
	  userId: string;
	  role: string;
	  joinedAt: number;
	}

	interface Message {
	  username: string;
	  message: string;
	}

	function generateUserId(): string {
	  return "user-" + Math.random().toString(36).slice(2, 11);
	}

	const chatRoom = actor({
	  state: { messages: [] as Message[] },

	  // Create connection state dynamically
	  createConnState: (c): ConnState => {
	    // Return the connection state
	    return {
	      userId: generateUserId(),
	      role: "guest",
	      joinedAt: Date.now()
	    };
	  },

	  actions: {
	    sendMessage: (c, message: string) => {
	      const username = c.conn.state.userId;
	      c.state.messages.push({ username, message });
	      c.broadcast("newMessage", { username, message });
	    }
	  }
	});
	```
</Tab>
</Tabs>

Connection Lifecycle

Each client connection goes through a series of lifecycle hooks that allow you to validate, initialize, and clean up connection-specific resources.

On Connect (per client)

  • onBeforeConnect
  • createConnState
  • onConnect

On Disconnect (per client)

  • onDisconnect

createConnState and connState

API Reference

There are two ways to define the initial state for connections:

  1. connState: Define a constant object that will be used as the initial state for all connections
  2. createConnState: A function that dynamically creates initial connection state based on connection parameters. Can be async.

onBeforeConnect

API Reference

The onBeforeConnect hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via params. This hook is used for connection validation and can throw errors to reject connections.

The onBeforeConnect hook does NOT return connection state - it's used solely for validation.

typescript
import { actor } from "rivetkit";

interface Message {
  text: string;
  author: string;
}

interface ConnParams {
  authToken?: string;
  userId?: string;
  role?: string;
}

interface ConnState {
  userId: string;
  role: string;
  joinTime: number;
}

function validateToken(token: string): boolean {
  return token.length > 0;
}

const chatRoom = actor({
  state: { messages: [] as Message[] },

  // Dynamically create connection state
  createConnState: (c, params: ConnParams): ConnState => {
    return {
      userId: params.userId || "anonymous",
      role: params.role || "guest",
      joinTime: Date.now()
    };
  },

  // Validate connections before accepting them
  onBeforeConnect: (c, params: ConnParams) => {
    // Validate authentication
    const authToken = params.authToken;
    if (!authToken || !validateToken(authToken)) {
      throw new Error("Invalid authentication");
    }

    // Authentication is valid, connection will proceed
    // The actual connection state will come from createConnState
  },

  actions: {}
});

Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication, see Authentication for details.

onConnect

API Reference

Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter.

typescript
import { actor } from "rivetkit";

interface ConnState {
  userId: string;
}

interface UserStatus {
  online: boolean;
  lastSeen: number;
}

const chatRoom = actor({
  state: { users: {} as Record<string, UserStatus>, messages: [] as string[] },

  createConnState: (): ConnState => ({
    userId: "user-" + Math.random().toString(36).slice(2, 11)
  }),

  onConnect: (c, conn) => {
    // Add user to the room's user list using connection state
    const userId = conn.state.userId;
    c.state.users[userId] = {
      online: true,
      lastSeen: Date.now()
    };

    // Broadcast that a user joined
    c.broadcast("userJoined", { userId, timestamp: Date.now() });

    console.log(`User ${userId} connected`);
  },

  actions: {}
});

Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect.

onDisconnect

API Reference

Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources.

typescript
import { actor } from "rivetkit";

interface ConnState {
  userId: string;
}

interface UserStatus {
  online: boolean;
  lastSeen: number;
}

const chatRoom = actor({
  state: { users: {} as Record<string, UserStatus>, messages: [] as string[] },

  createConnState: (): ConnState => ({
    userId: "user-" + Math.random().toString(36).slice(2, 11)
  }),

  onDisconnect: (c, conn) => {
    // Update user status when they disconnect
    const userId = conn.state.userId;
    if (c.state.users[userId]) {
      c.state.users[userId].online = false;
      c.state.users[userId].lastSeen = Date.now();
    }

    // Broadcast that a user left
    c.broadcast("userLeft", { userId, timestamp: Date.now() });

    console.log(`User ${userId} disconnected`);
  },

  actions: {}
});

Connection List

All active connections can be accessed through the context object's conns property. This is an array of all current connections.

This is frequently used with conn.send(name, event) to send messages directly to clients. To send an event to all connections at once, use c.broadcast() instead. See Events for more details on broadcasting.

For example:

typescript
import { actor } from "rivetkit";

interface ConnState {
  userId: string;
}

const chatRoom = actor({
  state: { users: {} as Record<string, { online: boolean }> },

  createConnState: (): ConnState => ({
    userId: "user-" + Math.random().toString(36).slice(2, 11)
  }),

  actions: {
    sendDirectMessage: (c, recipientId: string, message: string) => {
      // Find the recipient's connection by iterating over the Map
      let recipientConn = null;
      for (const conn of c.conns.values()) {
        if (conn.state.userId === recipientId) {
          recipientConn = conn;
          break;
        }
      }

      if (recipientConn) {
        // Send a private message to just that client
        recipientConn.send("directMessage", {
          from: c.conn.state.userId,
          message: message
        });
      }
    }
  }
});
<Note> `conn.send()` has no effect on [low-level WebSocket connections](/docs/actors/websocket-handler). For low-level WebSockets, use the WebSocket API directly (e.g., `websocket.send()`). </Note>

Disconnecting clients

Connections can be disconnected from within an action:

typescript
import { actor } from "rivetkit";

interface ConnState {
  userId: string;
}

const secureRoom = actor({
  state: {},

  createConnState: (): ConnState => ({
    userId: "user-" + Math.random().toString(36).slice(2, 11)
  }),

  actions: {
    kickUser: (c, targetUserId: string, reason?: string) => {
      // Find the connection to kick by iterating over the Map
      for (const conn of c.conns.values()) {
        if (conn.state.userId === targetUserId) {
          // Disconnect with a reason
          conn.disconnect(reason || "Kicked by admin");
          break;
        }
      }
    }
  }
});

If you need to wait for the disconnection to complete, you can use await:

typescript
import { actor } from "rivetkit";

const myActor = actor({
  state: {},
  actions: {
    disconnect: async (c) => {
      await c.conn.disconnect("Too many requests");
    }
  }
});

This ensures the underlying network connections close cleanly before continuing.

API Reference