# 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).
