Domain.Api reference
Source:
packages/domain/src/Api.ts
The public surface of a domain for one kind of client: the commands it may
send, the views and streams it may read, and the queries it may ask. Nothing in an Api needs a handler. The class is an Effect: yield* MyApi
gives a Worker its fetch. Call it with a typed client over
Domain.protocolHttp, Domain.protocolWebSocket or
Domain.protocolInProcess, or with clientHttp, clientWebSocket or
clientInProcess, which provide them.
Api: Declaring an Api
Section titled “Api: Declaring an Api”import * as Domain from "@alchemy.run/domain";import { Account, DepositMoney, WithdrawMoney } from "./Account.ts";import { AccountSummary } from "./AccountSummary.ts";import { Bank } from "./Bank.ts";
// Serving something Bank does not hold is a type error; nothing unlisted is served.export class CustomerApi extends Domain.Api("CustomerApi", { model: Bank, commands: ["DepositMoney", "WithdrawMoney"], views: ["AccountSummary"],}) {}Api: Serving it from a Worker
Section titled “Api: Serving it from a Worker”import * as Cloudflare from "@alchemy.run/cloudflare";import * as DomainCloudflare from "@alchemy.run/cloudflare/Domain";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import { Bank, BankPolicies } from "./Bank.ts";import { CustomerApi } from "./CustomerApi.ts";import { SessionLive } from "./Session.ts";
const BankLive = SessionLive.pipe( Layer.provideMerge(Bank.layer(BankPolicies)), Layer.provide(DomainCloudflare.layerDurableObjects()),);
export default class BankWorker extends Cloudflare.Worker<BankWorker>()( "BankWorker", { main: import.meta.url }, Effect.gen(function* () { const api = yield* CustomerApi; return { fetch: api.fetch }; }).pipe(Effect.provide(BankLive)),) {}Api: Calling it
Section titled “Api: Calling it”Domain.protocolWebSocket sends commands, get and list over HTTP, and
opens a WebSocket for each watch and events, pushed by the object that
owns the key. Domain.protocolHttp sends everything over HTTP.
const bank = yield* CustomerApi.client;const { reply } = yield* bank.DepositMoney(accountId, { amount: 1_000 });const summary = yield* bank.AccountSummary.get(accountId);yield* bank.AccountSummary.watch(accountId).pipe( Stream.runForEach(({ state }) => Effect.log(state?.balance)),);Api: Calling it in process
Section titled “Api: Calling it in process”clientInProcess serves the Api in the caller’s process, through the
middleware of everything it serves, with nothing sent over a network. Code that has
already authenticated its caller provides a middleware Layer that trusts it.
const asSystem = Layer.succeed(Session, (effect) => Effect.provideService(effect, CurrentActor, system("payments-webhook")),);const bank = yield* CustomerApi.clientInProcess.pipe(Effect.provide(asSystem));yield* bank.DepositMoney(accountId, { amount: 1_000 });Api: Annotating Apis
Section titled “Api: Annotating Apis”The last argument is its annotations: data for what is built on the
model, read back as X.annotations. Add your own keys by augmenting
Domain.Annotations.
Its description heads the CLI’s root help and is the OpenAPI document’s
info.description.
export class CustomerApi extends Domain.Api( "CustomerApi", { model: Bank, commands: ["DepositMoney"], views: ["AccountSummary"] }, { description: "The bank, as a customer reaches it." },) {}