Skip to content
// EFFECT DAYS 2026

RpcServer unstable

Runs server-side handlers for RPC groups.

This module connects typed handlers for an RpcGroup to a server Protocol. It receives client messages, decodes request payloads, runs matching handlers and middleware, tracks in-flight requests, handles acknowledgements and interrupts, and sends responses back to clients. It also provides constructors and layers for decoded messages, HTTP, WebSocket, sockets, stdio, and worker runner protocols.

20 exports Since v4.0.0 Source

Constructors

Since v4.0.0

Creates an RPC server for an already-decoded message channel, running handlers for a group and sending decoded server responses through onFromServer.

Details

If writing a stream Chunk fails with anything other than an interruption, the request fails with that cause. onFromServer is assumed to have already answered the client and reported it, so nothing else is sent or reported.

A request with isNotification set runs like any other, but no Exit, Chunk or Defect is sent for it. Its failures are still reported.

A request with discard set is acknowledged with a successful Exit once its handler starts, after the concurrency permit is taken and the middleware has run. Nothing else is sent for it after that, and its failures are only reported. A failure before the acknowledgement is sent as a failed Exit for that request.

Signature

declare const makeNoSerialization: <Rpcs extends Rpc.Any>(group: RpcGroup.RpcGroup<Rpcs>, options: {
readonly concurrency?: number | "unbounded";
readonly disableClientAcks?: boolean;
readonly disableFatalDefects?: boolean;
readonly disableSpanPropagation?: boolean;
readonly disableTracing?: boolean;
readonly onFromServer: (response: FromServer<Rpcs>) => Effect.Effect<void>;
readonly spanAttributes?: Record<string, unknown>;
readonly spanPrefix?: string;
}) => Effect.Effect<RpcServer<Rpcs>, never, Rpc.ToHandler<Rpcs> | Rpc.Middleware<Rpcs> | Scope.Scope>

Layers

layer

unstable
Since v4.0.0

Provides a scoped layer that starts an RPC server for a group using the current server Protocol.

Signature

declare function layer<Rpcs extends Any>(group: RpcGroup<Rpcs>, options?: {
readonly concurrency?: number | "unbounded";
readonly disableFatalDefects?: boolean;
readonly disableTracing?: boolean;
readonly onDisconnect?: (clientId: number) => Effect<void>;
readonly spanAttributes?: Record<string, unknown>;
readonly spanPrefix?: string;
}): Layer<never, never, Protocol | ToHandler<Rpcs> | Middleware<Rpcs> | ServicesServer<Rpcs>>

layerHttp

unstable
Since v4.0.0

Creates a RPC server that registers a HTTP route with a HttpRouter.

Details

Defaults to using websockets for communication, but can be configured to use HTTP.

Signature

declare function layerHttp<Rpcs extends Any>(options: {
readonly concurrency?: number | "unbounded";
readonly disableFatalDefects?: boolean;
readonly disableTracing?: boolean;
readonly group: RpcGroup<Rpcs>;
readonly onDisconnect?: (clientId: number) => Effect<void>;
readonly path: PathInput;
readonly protocol?: "http" | "websocket";
readonly spanAttributes?: Record<string, unknown>;
readonly spanPrefix?: string;
readonly streamBufferSize?: number | "unbounded";
}): Layer<never, never, HttpRouter | RpcSerialization | ToHandler<Rpcs> | Middleware<Rpcs> | ServicesServer<Rpcs>>
Since v4.0.0

Provides a server Protocol that uses HTTP POST requests for RPC communication.

Signature

declare function layerProtocolHttp(options: {
readonly path: PathInput;
readonly streamBufferSize?: number | "unbounded";
}): Layer<Protocol, never, HttpRouter | RpcSerialization>

RPC protocol that uses SocketServer for communication.

Signature

declare const layerProtocolSocketServer: Layer.Layer<Protocol, never, RpcSerialization.RpcSerialization | SocketServer.SocketServer>
Since v4.0.0

Provides a server Protocol that reads RPC messages from Stdio.stdin and writes encoded responses to Stdio.stdout.

Signature

declare const layerProtocolStdio: Layer.Layer<Protocol, never, RpcSerialization.RpcSerialization | Stdio>

RPC protocol that uses WebSockets for communication.

Signature

declare function layerProtocolWebsocket(options: {
readonly path: PathInput;
}): Layer<Protocol, never, HttpRouter | RpcSerialization>

Provides a server Protocol backed by the current WorkerRunnerPlatform.

Signature

declare const layerProtocolWorkerRunner: Layer.Layer<Protocol, WorkerError, WorkerRunner.WorkerRunnerPlatform>

Models

RpcServer

interface unstable
Since v4.0.0

The decoded RPC server boundary, accepting client messages for a client id and allowing that client to be disconnected.

Signature

interface RpcServer<A extends Rpc.Any> {
readonly disconnect: (clientId: number) => Effect<void>;
readonly write: (clientId: number, message: FromClient<A>, options?: {
readonly onRequest?: <A, E, R>(effect: Effect<A, E, R>) => Effect<A, E, R>;
}) => Effect<void>;
}

Protocols

Since v4.0.0

Creates an HTTP server Protocol and registers its request handler as a POST route on the current HttpRouter.

Signature

declare const makeProtocolHttp: (options: {
readonly path: HttpRouter.PathInput;
readonly streamBufferSize?: number | "unbounded";
}) => Effect.Effect<Protocol["Service"], never, RpcSerialization.RpcSerialization | HttpRouter.HttpRouter>

Creates a server Protocol backed by the current SocketServer, accepting socket connections and routing decoded RPC messages.

Signature

declare const makeProtocolSocketServer: Effect<{
readonly clientIds: Effect<ReadonlySet<number>>;
readonly codecFor: CodecFor;
readonly disconnects: Dequeue<number>;
readonly end: (clientId: number) => Effect<void>;
readonly initialMessage: Effect<Option<unknown>>;
readonly run: (f: (clientId: number, data: FromClientEncoded) => Effect<void>) => Effect<never>;
readonly send: (clientId: number, response: FromServerEncoded, transferables?: readonly Array<Transferable>) => Effect<void>;
readonly supportsAck: boolean;
readonly supportsNotifications: boolean;
readonly supportsSpanPropagation: boolean;
readonly supportsTransferables: boolean;
}, never, Scope | RpcSerialization | SocketServer>
Since v4.0.0

Creates a server Protocol that reads RPC messages from Stdio.stdin and writes encoded responses to Stdio.stdout.

Signature

declare const makeProtocolStdio: Effect<{
readonly clientIds: Effect<ReadonlySet<number>>;
readonly codecFor: CodecFor;
readonly disconnects: Dequeue<number>;
readonly end: (clientId: number) => Effect<void>;
readonly initialMessage: Effect<Option<unknown>>;
readonly run: (f: (clientId: number, data: FromClientEncoded) => Effect<void>) => Effect<never>;
readonly send: (clientId: number, response: FromServerEncoded, transferables?: readonly Array<Transferable>) => Effect<void>;
readonly supportsAck: boolean;
readonly supportsNotifications: boolean;
readonly supportsSpanPropagation: boolean;
readonly supportsTransferables: boolean;
}, never, Scope | Stdio | RpcSerialization>
Since v4.0.0

Creates a websocket server Protocol and registers its upgrade handler as a GET route on the current HttpRouter.

Signature

declare const makeProtocolWebsocket: (options: {
readonly path: HttpRouter.PathInput;
}) => Effect.Effect<Protocol["Service"], never, RpcSerialization.RpcSerialization | HttpRouter.HttpRouter>

Creates an HTTP request/response server Protocol together with an HTTP effect that decodes the current request and streams or returns encoded RPC responses.

Signature

declare const makeProtocolWithHttpEffect: (options?: {
readonly streamBufferSize?: number | "unbounded";
}) => Effect.Effect<{
readonly httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, never, Scope.Scope | HttpServerRequest.HttpServerRequest>;
readonly protocol: Protocol["Service"];
}, never, RpcSerialization.RpcSerialization>

Creates a websocket server Protocol together with an HTTP effect that upgrades the current request to a websocket and attaches it to the protocol.

Signature

declare const makeProtocolWithHttpEffectWebsocket: Effect.Effect<{
readonly httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, never, Scope.Scope | HttpServerRequest.HttpServerRequest>;
readonly protocol: Protocol["Service"];
}, never, RpcSerialization.RpcSerialization>

Creates a server Protocol backed by WorkerRunnerPlatform, routing worker messages to the RPC server and server responses back to workers.

Signature

declare const makeProtocolWorkerRunner: Effect.Effect<Protocol["Service"], WorkerError, WorkerRunner.WorkerRunnerPlatform | Scope.Scope>

Running

make

unstable
Since v4.0.0

Runs an RPC server for a group using the current server Protocol, decoding requests, invoking handlers, encoding responses, and managing in-flight request lifetime.

Details

The server consumes Protocol.disconnects to interrupt a disconnected client's requests, so use the onDisconnect option to observe disconnects instead of taking from that queue.

onDisconnect is called for each disconnect reported by the Protocol, after the server has requested interruption of that client's in-flight requests. It does not wait for those requests to finish. It is not called when a client ends its input normally, or for clients still connected when the server stops.

Each callback runs in its own fiber, so a slow callback does not delay cleanup for other clients, and callbacks may run concurrently. Pending callbacks are interrupted when the server stops. Failures are ignored and logged at References.UnhandledLogLevel.

Signature

declare const make: <Rpcs extends Rpc.Any>(group: RpcGroup.RpcGroup<Rpcs>, options?: {
readonly concurrency?: number | "unbounded";
readonly disableFatalDefects?: boolean;
readonly disableTracing?: boolean;
readonly onDisconnect?: (clientId: number) => Effect.Effect<void>;
readonly spanAttributes?: Record<string, unknown>;
readonly spanPrefix?: string;
}) => Effect.Effect<never, never, Protocol | Rpc.ToHandler<Rpcs> | Rpc.Middleware<Rpcs> | Rpc.ServicesServer<Rpcs>>
Since v4.0.0

Starts an RPC server for a group and returns the HTTP request/response effect that serves the non-websocket HTTP RPC protocol.

Signature

declare const toHttpEffect: <Rpcs extends Rpc.Any>(group: RpcGroup.RpcGroup<Rpcs>, options?: {
readonly disableFatalDefects?: boolean;
readonly disableTracing?: boolean;
readonly onDisconnect?: (clientId: number) => Effect.Effect<void>;
readonly spanAttributes?: Record<string, unknown>;
readonly spanPrefix?: string;
readonly streamBufferSize?: number | "unbounded";
}) => Effect.Effect<Effect.Effect<HttpServerResponse.HttpServerResponse, never, Scope.Scope | HttpServerRequest.HttpServerRequest>, never, Scope.Scope | RpcSerialization.RpcSerialization | Rpc.ToHandler<Rpcs> | Rpc.Middleware<Rpcs> | Rpc.ServicesServer<Rpcs>>
Since v4.0.0

Starts an RPC server for a group and returns the HTTP effect that upgrades requests to the websocket RPC protocol.

Signature

declare const toHttpEffectWebsocket: <Rpcs extends Rpc.Any>(group: RpcGroup.RpcGroup<Rpcs>, options?: {
readonly disableFatalDefects?: boolean;
readonly disableTracing?: boolean;
readonly onDisconnect?: (clientId: number) => Effect.Effect<void>;
readonly spanAttributes?: Record<string, unknown>;
readonly spanPrefix?: string;
}) => Effect.Effect<Effect.Effect<HttpServerResponse.HttpServerResponse, never, Scope.Scope | HttpServerRequest.HttpServerRequest>, never, Scope.Scope | RpcSerialization.RpcSerialization | Rpc.ToHandler<Rpcs> | Rpc.Middleware<Rpcs> | Rpc.ServicesServer<Rpcs>>

Services

Protocol

unstable
Since v4.0.0

Defines the service interface for an RPC server transport, responsible for receiving encoded client messages, sending encoded responses, tracking clients, and declaring transport capabilities.

When to use

Use to provide the transport boundary for RPC servers over HTTP, WebSocket, workers, sockets, or custom protocols.

Signature

declare class Protocol extends Shape<"effect/rpc/RpcServer/Protocol", {
readonly clientIds: Effect<ReadonlySet<number>>;
readonly codecFor: CodecFor;
readonly disconnects: Dequeue<number>;
readonly end: (clientId: number) => Effect<void>;
readonly initialMessage: Effect<Option<unknown>>;
readonly run: (f: (clientId: number, data: FromClientEncoded) => Effect<void>) => Effect<never>;
readonly send: (clientId: number, response: FromServerEncoded, transferables?: readonly Array<Transferable>) => Effect<void>;
readonly supportsAck: boolean;
readonly supportsNotifications: boolean;
readonly supportsSpanPropagation: boolean;
readonly supportsTransferables: boolean;
}, this> {
constructor(_: never);
static make: <EX, RX>(f: (write: (clientId: number, data: FromClientEncoded) => Effect<void>) => Effect<Omit<{
readonly clientIds: Effect<ReadonlySet<number>>;
readonly codecFor: CodecFor;
readonly disconnects: Dequeue<number>;
readonly end: (clientId: number) => Effect<void>;
readonly initialMessage: Effect<Option<unknown>>;
readonly run: (f: (clientId: number, data: FromClientEncoded) => Effect<void>) => Effect<never>;
readonly send: (clientId: number, response: FromServerEncoded, transferables?: readonly Array<Transferable>) => Effect<void>;
readonly supportsAck: boolean;
readonly supportsNotifications: boolean;
readonly supportsSpanPropagation: boolean;
readonly supportsTransferables: boolean;
}, "run">, EX, RX>) => Effect<{
readonly clientIds: Effect<ReadonlySet<number>>;
readonly codecFor: CodecFor;
readonly disconnects: Dequeue<number>;
readonly end: (clientId: number) => Effect<void>;
readonly initialMessage: Effect<Option<unknown>>;
readonly run: (f: (clientId: number, data: FromClientEncoded) => Effect<void>) => Effect<never>;
readonly send: (clientId: number, response: FromServerEncoded, transferables?: readonly Array<Transferable>) => Effect<void>;
readonly supportsAck: boolean;
readonly supportsNotifications: boolean;
readonly supportsSpanPropagation: boolean;
readonly supportsTransferables: boolean;
}, EX, RX>;
}