# RPC

The `effect/unstable/rpc` modules let you describe a set of remote procedures
**once**, as schema-backed definitions, and then derive both a typed client and
a typed server from that single description. The client method signatures, the
server handler signatures, the wire encoding, and the error channel are all
generated from the same `RpcGroup` — so the two ends can never drift out of
sync, and a payload or success type change is a compile error rather than a
runtime surprise.

```ts
import { Schema } from "effect"
import { Rpc, RpcGroup } from "effect/unstable/rpc"

// A single shared contract. The client and server both import this group,
// guaranteeing they agree on tags, payloads, results, and errors.
class UserNotFound extends Schema.TaggedErrorClass<UserNotFound>()(
  "UserNotFound",
  { id: Schema.String }
) {}

export class UserRpcs extends RpcGroup.make(
  Rpc.make("GetUser", {
    payload: { id: Schema.String }, // what the client sends
    success: Schema.Struct({ id: Schema.String, name: Schema.String }),
    error: UserNotFound // typed, schema-encoded failure
  })
) {}
```

An RPC definition is just **data**. Nothing is sent or handled until the group
is interpreted: a [client](https://effect.plants.sh/rpc/client-and-server/) reads the schemas to encode
requests and decode responses, and a [server](https://effect.plants.sh/rpc/client-and-server/) reads the
same schemas to decode requests, run your handlers, and encode results. The
transport (HTTP, websocket, socket, worker, or an in-memory test harness) is
chosen separately and never affects the typed surface.

:::caution[Unstable module]
The `effect/unstable/rpc` modules are still stabilizing. Import paths, option
names, and signatures may change between minor releases. Everything described
here is reconciled against the current `effect-smol` source, but pin your
version if you depend on the wire format.
:::

## When to use RPC

RPC is the right tool when you control both ends and want a function-call feel
across a process boundary — a backend service consumed by your own frontend, a
worker thread, or a child process. It gives you typed payloads, typed errors,
streaming results, and middleware without writing any serialization or routing
code by hand.

If instead you need a public, REST-shaped HTTP surface with explicit paths,
methods, and status codes — for third-party consumers or OpenAPI — reach for the
[HTTP API](https://effect.plants.sh/http-api/) modules. RPC and HTTP API share the same `Schema` and
`Effect` foundations, so the mental model carries over.

## The four pieces

Everything in this section imports from `effect/unstable/rpc`. A working RPC
setup is built from four roles:

- **The contract** — `Rpc` and `RpcGroup`. `Rpc.make` declares one procedure
  (tag, payload, success, error, optional streaming); `RpcGroup.make` collects
  procedures into a single typed group that both ends import. This is pure data.
- **The implementation** — `RpcServer` plus handlers. You attach a handler to
  every procedure in the group (`group.toLayer` / `group.toHandlers`), then
  `RpcServer` decodes incoming requests, runs the matching handler, and encodes
  the results back over the transport.
- **The caller** — `RpcClient`. `RpcClient.make` turns the same group into an
  object of callable methods; each method encodes its payload, sends a request,
  and decodes the response into an `Effect` (or a `Stream` for streaming RPCs).
- **The wire** — `RpcSerialization` plus a transport `Protocol`. Serialization
  (`RpcSerialization.layerJson`, `layerNdjson`, `layerMsgPack`) chooses how
  values are encoded as bytes; the protocol layer (HTTP, websocket, socket,
  worker, or `RpcTest` in memory) chooses how those bytes move between client
  and server. Neither affects the typed surface from the contract.

These four roles also underpin higher-level runtimes: [Cluster](https://effect.plants.sh/cluster/)
entities, for example, are defined as `RpcGroup`s and reuse the same
handler/client machinery.

## In this section

- **[Defining RPCs](https://effect.plants.sh/rpc/defining-rpcs/)** — declare a procedure with `Rpc.make`,
  attach payload, success, and error schemas, model streaming results with
  `RpcSchema.Stream`, and compose procedures into an `RpcGroup` (`add`, `merge`,
  `prefix`, `annotateRpcs`).
- **[Client and server](https://effect.plants.sh/rpc/client-and-server/)** — implement handlers with
  `group.toLayer`, run them with `RpcServer`, build a typed client with
  `RpcClient`, and test the whole thing in-memory with `RpcTest`.
- **[Middleware](https://effect.plants.sh/rpc/middleware/)** — cross-cutting concerns with
  `RpcMiddleware`: authentication, logging, context propagation, and adding
  middleware error types to both the client and server signatures.
- **[Transports and serialization](https://effect.plants.sh/rpc/transports-and-serialization/)** — wire
  the contract to a real channel with `RpcSerialization` (JSON, NDJSON, MsgPack)
  and the protocol layers for HTTP, websocket, socket, stdio, and worker.
- **[Advanced RPC](https://effect.plants.sh/rpc/advanced/)** — streaming back pressure, `asQueue`
  clients, defect handling, request headers, primary-key payloads, and the
  `makeNoSerialization` escape hatch for already-decoded channels.

## Related sections

RPC builds directly on concepts covered elsewhere: [Schema](https://effect.plants.sh/schema/) for the
wire contract, [Error Management](https://effect.plants.sh/error-management/) for typed failures,
[Services & Layers](https://effect.plants.sh/services-and-layers/) for wiring, [Streaming](https://effect.plants.sh/streaming/)
for streaming RPCs, and the [Platform](https://effect.plants.sh/platform/) / [HTTP Client](https://effect.plants.sh/http-client/)
modules for transports. [Cluster](https://effect.plants.sh/cluster/) builds distributed entities on top
of RPC groups.