Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

EntityProxy unstable

Derives RPC and HTTP API surfaces from clustered entities.

The generated APIs let callers use ordinary RPC clients or HTTP routes while the cluster runtime still locates and delivers messages to the entity instance identified by entityId. Each generated operation keeps the original payload and success schema, adds cluster client errors, and creates a discard variant for fire-and-forget delivery.

4 exports Since v4.0.0 Source

Constructors

Since v4.0.0

Derives an HttpApiGroup from an Entity.

Signature

declare function toHttpApiGroup<Name extends string, Type extends string, Rpcs extends Any>(name: Name, entity: Entity<Type, Rpcs>): HttpApiGroup<Name, ConvertHttpApi<Rpcs>>

Example Deriving HTTP API endpoints from an entity

import { Layer, Schema } from "effect"
import { ClusterSchema, Entity, EntityProxy, EntityProxyServer } from "effect/cluster"
import { HttpApi, HttpApiBuilder } from "effect/http-api"
import { Rpc } from "effect/rpc"
export const Counter = Entity.make("Counter", [
Rpc.make("Increment", {
payload: { id: Schema.String, amount: Schema.Number },
primaryKey: ({ id }) => id,
success: Schema.Number
})
]).annotateRpcs(ClusterSchema.Persisted, true)
// Use EntityProxy.toHttpApiGroup to create a `HttpApiGroup` from the
// Counter entity
export class MyApi extends HttpApi.make("api")
.add(
EntityProxy.toHttpApiGroup("counter", Counter)
.prefix("/counter")
)
{}
// Use EntityProxyServer.layerHttpApi to create a layer that implements
// the handlers for the HttpApiGroup
const ApiLayer = HttpApiBuilder.layer(MyApi).pipe(
Layer.provide(EntityProxyServer.layerHttpApi(MyApi, "counter", Counter))
)
const result = [Object.keys(MyApi.groups.counter.endpoints).length, Layer.isLayer(ApiLayer)] // => [2, true]

toRpcGroup

unstable
Since v4.0.0

Derives an RpcGroup from an Entity.

Signature

declare function toRpcGroup<Type extends string, Rpcs extends Any>(entity: Entity<Type, Rpcs>): RpcGroup<ConvertRpcs<Rpcs, Type>>

Example Deriving RPC endpoints from an entity

import { Layer, Schema } from "effect"
import { ClusterSchema, Entity, EntityProxy, EntityProxyServer } from "effect/cluster"
import { Rpc, RpcServer } from "effect/rpc"
export const Counter = Entity.make("Counter", [
Rpc.make("Increment", {
payload: { id: Schema.String, amount: Schema.Number },
primaryKey: ({ id }) => id,
success: Schema.Number
})
]).annotateRpcs(ClusterSchema.Persisted, true)
// Use EntityProxy.toRpcGroup to create a `RpcGroup` from the Counter entity
export class MyRpcs extends EntityProxy.toRpcGroup(Counter) {}
// Use EntityProxyServer.layerRpcHandlers to create a layer that implements
// the rpc handlers
const RpcServerLayer = RpcServer.layer(MyRpcs).pipe(
Layer.provide(EntityProxyServer.layerRpcHandlers(Counter))
)
const result = [MyRpcs.requests.size, Layer.isLayer(RpcServerLayer)] // => [2, true]

Converting

ConvertHttpApi

type unstable
Since v4.0.0

Type-level conversion used by toHttpApiGroup.

Details

For each entity RPC, this creates a POST endpoint at /<tag>/:entityId and a discard endpoint at /<tag>/:entityId/discard, including cluster client errors.

Signature

type ConvertHttpApi<Rpcs extends Rpc.Any> = Rpcs extends Rpc.Rpc<infer _Tag, infer _Payload, infer _Success, infer _Error, infer _Middleware, infer _Requires> ? HttpApiEndpoint.HttpApiEndpoint<_Tag, "POST", `/${Lowercase<_Tag>}/:entityId`, Schema.Struct<{
entityId: typeof EntityId;
}>, never, _Payload, never, _Success, _Error | typeof MailboxFull | typeof AlreadyProcessingMessage | typeof PersistenceError | typeof EntityNotAssignedToRunner> | HttpApiEndpoint.HttpApiEndpoint<`${_Tag}Discard`, "POST", `/${Lowercase<_Tag>}/:entityId/discard`, Schema.Struct<{
entityId: typeof EntityId;
}>, never, _Payload, never, Schema.Void, typeof MailboxFull | typeof AlreadyProcessingMessage | typeof PersistenceError> : never

ConvertRpcs

type unstable
Since v4.0.0

Type-level conversion used by toRpcGroup.

Details

For each entity RPC, this creates a prefixed request RPC and a discard RPC whose payload includes entityId, and whose errors include cluster client errors.

Signature

type ConvertRpcs<Rpcs extends Rpc.Any, Prefix extends string> = Rpcs extends Rpc.Rpc<infer _Tag, infer _Payload, infer _Success, infer _Error, infer _Middleware, infer _Requires> ? Rpc.Rpc<`${Prefix}.${_Tag}`, Schema.Struct<{
entityId: typeof Schema.String;
payload: _Payload;
}>, _Success, Schema.Codec<_Error["Type"] | MailboxFull | AlreadyProcessingMessage | PersistenceError | EntityNotAssignedToRunner, _Error["Encoded"] | typeof MailboxFull["Encoded"] | typeof AlreadyProcessingMessage["Encoded"] | typeof PersistenceError["Encoded"] | typeof EntityNotAssignedToRunner["Encoded"], _Error["DecodingServices"], _Error["EncodingServices"]>> | Rpc.Rpc<`${Prefix}.${_Tag}Discard`, Schema.Struct<{
entityId: typeof Schema.String;
payload: _Payload;
}>, typeof Schema.Void, Schema.Union<[typeof MailboxFull, typeof AlreadyProcessingMessage, typeof PersistenceError]>> : never