Skip to main content

guides/clients.md

# Clients

Clients run in the calling BEAM at runtime.

## One-shot calls

A one-shot call opens a socket, sends one request, waits for one reply, and closes the socket.

```elixir
{:ok, status} = SafeRPC.call("/run/my-app/rpc.sock", {MyApp.AdminRPC, :status})
```

Pass a payload and metadata when needed:

```elixir
SafeRPC.call(socket, {MyApp.AdminRPC, :lookup}, %{id: "123"}, meta: %{trace_id: "abc"})
```

## Persistent clients

Use a persistent client process when making multiple calls:

```elixir
{:ok, client} = SafeRPC.Client.start_link(socket: "/run/my-app/rpc.sock")

{:ok, status} = SafeRPC.call(client, {MyApp.AdminRPC, :status})
{:ok, item} = SafeRPC.call(client, {MyApp.AdminRPC, :lookup}, %{id: "123"})
```

## Casts

Casts send a request and return the server acknowledgement:

```elixir
{:ok, :noreply} = SafeRPC.cast(client, {MyApp.AdminRPC, :refresh}, %{scope: :all})
```

## Async requests

```elixir
task = SafeRPC.async(client, {MyApp.AdminRPC, :slow_report}, %{range: "24h"})
{:ok, report} = SafeRPC.await(task, 10_000)
```

Use `yield/2` and `cancel/1` for non-blocking waits and cancellation.

## Atom preparation

If replies can contain atoms not yet present in the client VM, prepare the client before the first atom-rich call:

```elixir
:ok = SafeRPC.prepare(client, max_atoms: 1_000, max_atom_length: 128)
```

Preparation asks the server for its compile-time vocabulary as strings, validates it on the client, and interns accepted atoms so later replies can be decoded with safe ETF.

See [Atom vocabularies and safe ETF](atom-vocabularies.md).