HTTP API
api() exposes selected commands and views over HTTP. serve() turns it into a fetch-style handler, and client() gives you a typed remote client.
Import
import { api, client, fail, ok, rejection, serve, wire } from '@marcelsud/accord'Usage
const Unauthorized = rejection('Unauthorized')
const PublicBank = api(
'PublicBank',
{
model: Bank,
commands: [Account.commands.open, Account.commands.deposit],
views: [Summary],
rejects: [Unauthorized],
},
(request) => {
if (request.headers.get('authorization') !== 'Bearer example') {
return fail(Unauthorized())
}
return ok({ actor: 'example-user' })
}
)
// Server
const fetchHandler = serve(PublicBank, { runtime: wire(Bank) })
// Client
const remote = client<typeof PublicBank>('https://your-server/api', {
headers: { authorization: 'Bearer example' },
})
const [summary, error] = await remote.summary.get('a-1')Account here is an event-evolved aggregate: only command() definitions can be exposed.
WARNING
The fixed token only illustrates the contract. Plug in your own authentication for a real endpoint.
api(name, contract, authorize?)
model
The model to expose.
commands
Command[]. Only these are exposed. An empty list exposes none.
views
View[].
rejects optional
Rejection[]. Authorization failures must be listed here.
uses optional
Record<string, Dependency>.
authorize optional
(request, ctx) => Result<{ actor? }, Rejection>. Receives the operation, target, input, and headers. The returned actor is the trusted command actor. Omitting it allows anonymous access to the selected operations.
serve(api, { runtime })
Returns (request: Request) => Promise<Response>. Mount it in any server that speaks Request/Response.
Requests are POST with a JSON body: { kind, operation, target, input, options }, where operation is alias.method.
client(url, { fetch?, headers? })
A typed client for commands and views. Pass a custom fetch to test without a server:
const remote = client<typeof PublicBank>('http://test/api', {
fetch: (input, init) => fetchHandler(new Request(input, init)),
})What's supported
- Commands, and view
get,snapshot, andevents. - Not supported: live subscriptions, workflows, streams, WebSocket, SSE, OpenAPI, or MCP.
- Server-side
Unexpectedcauses and stacks are stripped. The client recreates anErrorwith the sanitized response as its cause. - Command selection is enforced by definition identity on the server. The static client type is structural, so commands with identical contract types may look the same to TypeScript, but the server still rejects every unlisted command.