> For the complete documentation index, see [llms.txt](https://tivet.gitbook.io/tivet-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tivet.gitbook.io/tivet-docs/build-with-actors/remote-procedure-calls.md).

# Remote Procedure Calls

### Writing RPCs <a href="#writing-rpcs" id="writing-rpcs"></a>

RPCs can be written as native JavaScript methods on the actor class.

For example, the multiplyByTwo method is written as:

```
export class Example extends Actor {
	// This is an RPC
	multiplyByTwo(rpc: Rpc<Example>, x: number) {
		return x * 2;
	}
}
```

#### Private Methods <a href="#private-methods" id="private-methods"></a>

Methods starting with \_ or # (e.g. \_myMethod and #myMethod) are private and cannot be called by clients.

All Tivet-provided methods start with \_ (e.g. \_broadcast) so clients cannot call them.

For example:

```
export default class Example extends Actor {
	// This is private and cannot be called by clients
	#calcFibonacci(n: number): number {
		if (n <= 1) return n;
		return this.#calcFibonacci(n - 1) + this.#calcFibonacci(n - 2);
	}

	// This is public and can be called by clients
	fetchFibonacci(rpc: Rpc<Example>, n: number): number {
		return this.#calcFibonacci(n);
	}
}
```

#### Streaming Return Data <a href="#streaming-return-data" id="streaming-return-data"></a>

RPCs have a single return value. In order to stream realtime data in response to an RPC.

***

### Calling RPCs <a href="#calling-rpcs" id="calling-rpcs"></a>

Calling RPCs is as simple as calling any other JavaScript function.

```
import type { Counter } from "./counter.ts";

const actor = await client.get<Counter>({ name: "counter" });
await actor.increment(42);
```

**Note**

Calling RPCs from the client are async and require an await, even if the actor's method is not async.

#### Type Safety <a href="#type-safety" id="type-safety"></a>

The Tivet client includes type safety out of the box. The first generic parameter in get\<Actor>(...) defines the actor class. You can safely import the actor's type with import type in to the client, like this:

```
import type { Counter } from "./counter.ts";
// ...setup...
const actor = await client.get<Counter>(/* ... */);
await actor.increment(123); // passes
await actor.increment("non-number type"); // compile error
await actor.nonexistentMethod(123); // compile error
```

***

### Error Handling <a href="#error-handling" id="error-handling"></a>

Tivet provides robust error handling out of the box for RPCs.

#### User Errors <a href="#user-errors" id="user-errors"></a>

UserError error can be used to return rich error data to the client. You can provide:

* A human-readable message
* A machine-readable code that's useful for matching errors in a try-catch (optional)
* A metadata object for providing richer error context (optional)

For example:

```
// Throw a simple error with a message
throw new UserError("Invalid username");

// Throw an error with a code
throw new UserError("Invalid username", {
	code: "invalid_username",
});

// Throw an error with custom metadata
throw new UserError("Invalid username", {
	code: "invalid_username",
	meta: {
		maxLength: 32,
	},
});
```

#### Internal Errors <a href="#internal-errors" id="internal-errors"></a>

All other errors will return an error with the code internal\_error to the client. This helps keep your application secure, as errors can sometimes expose sensitive information.

***

### Schema Validation <a href="#schema-validation" id="schema-validation"></a>

Data schemas are not validated by default. For production applications, use a library like [zod](https://zod.dev/) to validate input types.

In the previous example, providing a non-number value to count could corrupt the actor's state (e.g. by passing a string instead of a number). For example, to validate the increment request schema:

```
import { z } from "zod";

// Define schemas for user requests
const IncrementOptionsSchema = z.object({
	count: z.number().int(),
});

type IncrementOptions = z.infer<typeof IncrementOptionsSchema>;

export class Counter extends Actor {
	increment(rpc: Rpc<Counter>, opts: IncrementOptions) {
		// Will throw error if input is invalid
		const validatedOpts = IncrementOptionsSchema.parse(opts);

		// ...etc...
	}
}
```

***

### Authentication <a href="#authentication" id="authentication"></a>

By default, clients can call all RPCs on an actor without restriction. Make sure to implement authentication if needed. Documentation on authentication is available [here](https://rivet.gg/docs/authentication).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tivet.gitbook.io/tivet-docs/build-with-actors/remote-procedure-calls.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
