# CommandApi

Serves an aggregate over HTTP with `effect/http-api`: one `POST` endpoint per command, and `GET` endpoints for `history` and, given a schema of the state, `state`.

```ts
const TodoApi = CommandApi.group(Todo, {
  state: TodoStateSchema,
  errors: [TodoAlreadyCreated, TodoNotFound, TodoAlreadyDeleted]
})


class Api extends HttpApi.make("api").add(TodoApi) {}


const TodoHandlers = CommandApi.layer(Api, "todo", AggregateService.make(Todo))
```

| Endpoint                             | Does                                                                                      |
| ------------------------------------ | ----------------------------------------------------------------------------------------- |
| `POST /todo/:aggregateId/createTodo` | Runs `createTodo` with the body as payload                                                |
| `GET /todo/:aggregateId`             | `state`, with an optional `finishTime`                                                    |
| `GET /todo/:aggregateId/history`     | `history`, with optional `payload`, `startTime`, `finishTime`, `afterVersion` and `limit` |

The group is an ordinary `HttpApiGroup`, so it takes middleware, a different prefix and OpenAPI annotations like any other, and `HttpApiClient` calls it.

A command’s response carries the store position of its last event, in the body and in an `Event-Position` header. [ReadAfter](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafter) is middleware for your own query endpoints: a request with a `Read-After` header waits until the view models have handled that position, so a client reads what it just wrote.

## Classes

### CommandRejected

Defined in: [src/CommandApi.ts:82](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L82)

A command handler failed with an error that `errors` doesn’t declare. Its tag and message are kept, and the rest of it is left out of the response.

#### Extends

* `object` & `YieldableError`<`this`>

#### Constructors

##### Constructor

> **new CommandRejected**(…`args`): [`CommandRejected`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#commandrejected)

Defined in: node_modules/.pnpm/effect\@4.0.6/node_modules/effect/dist/Schema.d.ts:10979

###### Parameters

###### args

…\[`props`: `object`, `options?`: `MakeOptions`]

###### Returns

[`CommandRejected`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#commandrejected)

###### Inherited from

`Schema.TaggedError<CommandRejected>()("CommandRejected", { aggregateName: Schema.String, command: Schema.String, /** The tag of the handler's error. */ error: Schema.String, message: Schema.String }, { httpApiStatus: 422 }).constructor`

#### Properties

##### aggregateName

> `readonly` **aggregateName**: `string` = `Schema.String`

Defined in: [src/CommandApi.ts:83](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L83)

###### Inherited from

`Schema.TaggedError<CommandRejected>()("CommandRejected", { aggregateName: Schema.String, command: Schema.String, /** The tag of the handler's error. */ error: Schema.String, message: Schema.String }, { httpApiStatus: 422 }).aggregateName`

##### command

> `readonly` **command**: `string` = `Schema.String`

Defined in: [src/CommandApi.ts:84](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L84)

###### Inherited from

`Schema.TaggedError<CommandRejected>()("CommandRejected", { aggregateName: Schema.String, command: Schema.String, /** The tag of the handler's error. */ error: Schema.String, message: Schema.String }, { httpApiStatus: 422 }).command`

##### error

> `readonly` **error**: `string` = `Schema.String`

Defined in: [src/CommandApi.ts:86](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L86)

The tag of the handler’s error.

###### Inherited from

`Schema.TaggedError<CommandRejected>()("CommandRejected", { aggregateName: Schema.String, command: Schema.String, /** The tag of the handler's error. */ error: Schema.String, message: Schema.String }, { httpApiStatus: 422 }).error`

##### message

> **message**: `string` = `Schema.String`

Defined in: [src/CommandApi.ts:87](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L87)

###### Inherited from

`Schema.TaggedError<CommandRejected>()("CommandRejected", { aggregateName: Schema.String, command: Schema.String, /** The tag of the handler's error. */ error: Schema.String, message: Schema.String }, { httpApiStatus: 422 }).message`

***

### NotCaughtUp

Defined in: [src/CommandApi.ts:375](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L375)

The view models didn’t reach the position a `Read-After` header asked for in time, because they are behind or stalled on a failing handler. Answered with `503`: the client can try again, or read without the header and accept an older answer.

#### Extends

* `object` & `YieldableError`<`this`>

#### Constructors

##### Constructor

> **new NotCaughtUp**(…`args`): [`NotCaughtUp`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#notcaughtup)

Defined in: node_modules/.pnpm/effect\@4.0.6/node_modules/effect/dist/Schema.d.ts:10979

###### Parameters

###### args

…\[`props`: `object`, `options?`: `MakeOptions`]

###### Returns

[`NotCaughtUp`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#notcaughtup)

###### Inherited from

`Schema.TaggedError<NotCaughtUp>()("NotCaughtUp", { /** The position the request asked for. */ position: Schema.Int, message: Schema.String }, { httpApiStatus: 503 }).constructor`

#### Properties

##### message

> **message**: `string` = `Schema.String`

Defined in: [src/CommandApi.ts:378](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L378)

###### Inherited from

`Schema.TaggedError<NotCaughtUp>()("NotCaughtUp", { /** The position the request asked for. */ position: Schema.Int, message: Schema.String }, { httpApiStatus: 503 }).message`

##### position

> `readonly` **position**: `number` = `Schema.Int`

Defined in: [src/CommandApi.ts:377](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L377)

The position the request asked for.

###### Inherited from

`Schema.TaggedError<NotCaughtUp>()("NotCaughtUp", { /** The position the request asked for. */ position: Schema.Int, message: Schema.String }, { httpApiStatus: 503 }).position`

***

### ReadAfter

Defined in: [src/CommandApi.ts:398](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L398)

Middleware for query endpoints that read view models. A request with a `Read-After: <position>` header waits until every view model of this process has handled that position, the one a command answered with, before the endpoint runs. A request without the header runs at once. A header that isn’t a position is answered with `400`.

```ts
const Catalog = HttpApiGroup.make("catalog")
  .add(HttpApiEndpoint.get("available", "/books/available", { success: Books }))
  .middleware(CommandApi.ReadAfter)


const ReadAfterLive = CommandApi.layerReadAfter({ timeout: "2 seconds" })
```

Implement it with [layerReadAfter](https://effect-cqrs.dambox.ch/reference/api/commandapi/#layerreadafter).

#### Extends

* `Shape`<`"effect-cqrs/CommandApi/ReadAfter"`, `HttpApiMiddleware`<`never`, readonly \[*typeof* [`NotCaughtUp`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#notcaughtup), *typeof* `BadRequest`], `never`>, `this`> & `object`

#### Constructors

##### Constructor

> **new ReadAfter**(`_`): [`ReadAfter`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafter)

Defined in: node_modules/.pnpm/effect\@4.0.6/node_modules/effect/dist/http-api/HttpApiMiddleware.d.ts:265

###### Parameters

###### \_

`never`

###### Returns

[`ReadAfter`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafter)

###### Inherited from

`HttpApiMiddleware.Service<ReadAfter>()("effect-cqrs/CommandApi/ReadAfter", { error: [NotCaughtUp, HttpApiError.BadRequest] }).constructor`

## Interfaces

### LayerOptions

Defined in: [src/CommandApi.ts:276](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L276)

#### Properties

##### historyLimit?

> `readonly` `optional` **historyLimit?**: `number`

Defined in: [src/CommandApi.ts:282](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L282)

The most events one `history` response lists, so that a long stream can’t make one response as large as the stream. A client reads further with `afterVersion`. Defaults to 1,000.

***

### Options

Defined in: [src/CommandApi.ts:176](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L176)

#### Type Parameters

##### Name

`Name` *extends* `string`

##### State

`State` *extends* `Schema.Top`

##### Errors

`Errors` *extends* `ReadonlyArray`<`Schema.Top`>

#### Properties

##### errors?

> `readonly` `optional` **errors?**: `Errors`

Defined in: [src/CommandApi.ts:192](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L192)

Errors of the command handlers to answer with as they are. Others become [CommandRejected](https://effect-cqrs.dambox.ch/reference/api/commandapi/#commandrejected). An error without an `httpApiStatus` annotation is answered with `422`.

##### name?

> `readonly` `optional` **name?**: `Name`

Defined in: [src/CommandApi.ts:182](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L182)

The identifier of the group, the aggregate’s name by default.

##### prefix?

> `readonly` `optional` **prefix?**: `` `/${string}` ``

Defined in: [src/CommandApi.ts:184](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L184)

Where the endpoints start, `/<aggregate name>` by default.

##### state?

> `readonly` `optional` **state?**: `State`

Defined in: [src/CommandApi.ts:186](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L186)

The schema of the state. Without it there is no `state` endpoint.

***

### ReadAfterOptions

Defined in: [src/CommandApi.ts:402](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L402)

#### Properties

##### timeout?

> `readonly` `optional` **timeout?**: `Input`

Defined in: [src/CommandApi.ts:404](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L404)

How long a request waits before it is answered with [NotCaughtUp](https://effect-cqrs.dambox.ch/reference/api/commandapi/#notcaughtup). Defaults to 5 seconds.

## Type Aliases

### Endpoints

> **Endpoints**<`A`, `State`, `Errors`> = `{ readonly [K in CommandName<A>]: CommandEndpoint<A, K, Errors> }`\[[`CommandName`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#commandname-1)<`A`>] | `HistoryEndpoint`<`A`> | \[`State`] *extends* \[`never`] ? `never` : `StateEndpoint`<`State`>

Defined in: [src/CommandApi.ts:171](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L171)

The endpoints of [group](https://effect-cqrs.dambox.ch/reference/api/commandapi/#group).

#### Type Parameters

##### A

`A` *extends* [`Any`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#any)

##### State

`State` *extends* `Schema.Top`

##### Errors

`Errors` *extends* `Schema.Top`

## Variables

### ConcurrencyConflictError

> `const` **ConcurrencyConflictError**: `decodeTo`<`declareConstructor`<[`ConcurrencyConflict`](https://effect-cqrs.dambox.ch/reference/api/eventstore/#concurrencyconflict), { `aggregateId`: `string`; `aggregateName`: `string`; `expectedVersion`: `number`; }, readonly \[`TaggedStruct`<`"ConcurrencyConflict"`, { `aggregateId`: `String`; `aggregateName`: `String`; `expectedVersion`: `Int`; }>], { `aggregateId`: `string`; `aggregateName`: `string`; `expectedVersion`: `number`; }>, `TaggedStruct`<`"ConcurrencyConflict"`, { `aggregateId`: `String`; `aggregateName`: `String`; `expectedVersion`: `Int`; }>, `never`, `never`>

Defined in: [src/CommandApi.ts:73](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L73)

The stream moved on while the command was decided, and retries ran out. Answered with `409`.

***

### InvalidCommandError

> `const` **InvalidCommandError**: `decodeTo`<`declareConstructor`<[`InvalidCommand`](https://effect-cqrs.dambox.ch/reference/api/aggregateservice/#invalidcommand), { `aggregateName`: `string`; `message`: `string`; `type`: `string`; }, readonly \[`TaggedStruct`<`"InvalidCommand"`, { `aggregateName`: `String`; `message`: `String`; `type`: `String`; }>], { `aggregateName`: `string`; `message`: `string`; `type`: `string`; }>, `TaggedStruct`<`"InvalidCommand"`, { `aggregateName`: `String`; `message`: `String`; `type`: `String`; }>, `never`, `never`>

Defined in: [src/CommandApi.ts:70](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L70)

A command whose payload doesn’t match its schema, or a body that isn’t JSON. The same error as `AggregateService.InvalidCommand`, answered with `400`.

***

### WrongExpectedVersionError

> `const` **WrongExpectedVersionError**: `decodeTo`<`declareConstructor`<[`WrongExpectedVersion`](https://effect-cqrs.dambox.ch/reference/api/aggregateservice/#wrongexpectedversion), { `actualVersion`: `number`; `aggregateId`: `string`; `aggregateName`: `string`; `expectedVersion`: `number`; }, readonly \[`TaggedStruct`<`"WrongExpectedVersion"`, { `actualVersion`: `Int`; `aggregateId`: `String`; `aggregateName`: `String`; `expectedVersion`: `Int`; }>], { `actualVersion`: `number`; `aggregateId`: `string`; `aggregateName`: `string`; `expectedVersion`: `number`; }>, `TaggedStruct`<`"WrongExpectedVersion"`, { `actualVersion`: `Int`; `aggregateId`: `String`; `aggregateName`: `String`; `expectedVersion`: `Int`; }>, `never`, `never`>

Defined in: [src/CommandApi.ts:76](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L76)

The stream is no longer at the version `If-Match` named. Answered with `409`.

## Functions

### commandResult()

> **commandResult**<`Events`>(`events`): `Struct`<{ `aggregateId`: `String`; `aggregateName`: `String`; `correlationId`: `String`; `events`: `$Array`<`Events`>; `position`: `Int`; `version`: `Int`; }>

Defined in: [src/CommandApi.ts:91](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L91)

The body of a successful command, `CommandResult` with the events in their JSON form.

#### Type Parameters

##### Events

`Events` *extends* [`EventSchema`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#eventschema)<`any`>

#### Parameters

##### events

`Events`

#### Returns

`Struct`<{ `aggregateId`: `String`; `aggregateName`: `String`; `correlationId`: `String`; `events`: `$Array`<`Events`>; `position`: `Int`; `version`: `Int`; }>

***

### group()

> **group**<`A`, `Name`, `State`, `Errors`>(`aggregate`, `options?`): `HttpApiGroup`<`Name`, [`Endpoints`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#endpoints)<`A`, `State`, `Errors`\[`number`]>>

Defined in: [src/CommandApi.ts:205](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L205)

Builds the endpoints of an aggregate. Add the group to an `HttpApi` and implement it with [layer](https://effect-cqrs.dambox.ch/reference/api/commandapi/#layer).

#### Type Parameters

##### A

`A` *extends* [`Any`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#any)

##### Name

`Name` *extends* `string` = `A`\[`"name"`]

##### State

`State` *extends* `Top` = `never`

##### Errors

`Errors` *extends* readonly `Top`\[] = \[]

#### Parameters

##### aggregate

`A`

##### options?

[`Options`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#options)<`Name`, `State`, `Errors`>

#### Returns

`HttpApiGroup`<`Name`, [`Endpoints`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#endpoints)<`A`, `State`, `Errors`\[`number`]>>

***

### historyEntry()

> **historyEntry**<`Events`>(`events`): `Struct`<{ `causationId`: `NullOr`<`String`>; `correlationId`: `NullOr`<`String`>; `metadata`: `Codec`<`JsonObject`, `JsonObject`, `never`, `never`>; `payload`: `optionalKey`<`Events`>; `timestamp`: `DateTimeUtc`; `type`: `String`; `version`: `Int`; }>

Defined in: [src/CommandApi.ts:102](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L102)

One entry of `history`. `payload` is there when the request asks for it.

#### Type Parameters

##### Events

`Events` *extends* [`EventSchema`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#eventschema)<`any`>

#### Parameters

##### events

`Events`

#### Returns

`Struct`<{ `causationId`: `NullOr`<`String`>; `correlationId`: `NullOr`<`String`>; `metadata`: `Codec`<`JsonObject`, `JsonObject`, `never`, `never`>; `payload`: `optionalKey`<`Events`>; `timestamp`: `DateTimeUtc`; `type`: `String`; `version`: `Int`; }>

***

### layer()

> **layer**<`ApiId`, `Groups`, `Name`, `A`, `E`, `R`>(`api`, `name`, `service`, `options?`): `Layer`<`Service`<`ApiId`, `Name`>, `E`, `Exclude`<`R`, `Scope`>>

Defined in: [src/CommandApi.ts:292](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L292)

Implements a group built by [group](https://effect-cqrs.dambox.ch/reference/api/commandapi/#group) with an aggregate service, or an effect that builds one when the layer starts.

Unexpected failures, such as a store that can’t be reached, are defects and answered with `500`.

#### Type Parameters

##### ApiId

`ApiId` *extends* `string`

##### Groups

`Groups` *extends* `Constraint`

##### Name

`Name` *extends* `string`

##### A

`A` *extends* [`Any`](https://effect-cqrs.dambox.ch/reference/api/aggregate/#any)

##### E

`E` = `never`

##### R

`R` = `never`

#### Parameters

##### api

`HttpApi`<`ApiId`, `Groups`>

##### name

`Name`

##### service

[`AggregateService`](https://effect-cqrs.dambox.ch/reference/api/aggregateservice/#aggregateservice)<`A`> | `Effect`<[`AggregateService`](https://effect-cqrs.dambox.ch/reference/api/aggregateservice/#aggregateservice)<`A`>, `E`, `R`>

##### options?

[`LayerOptions`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#layeroptions)

#### Returns

`Layer`<`Service`<`ApiId`, `Name`>, `E`, `Exclude`<`R`, `Scope`>>

***

### layerReadAfter()

> **layerReadAfter**(`options?`): `Layer`<[`ReadAfter`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafter), `never`, [`ViewModels`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#viewmodels)>

Defined in: [src/CommandApi.ts:415](https://github.com/davidnussio/effect-cqrs/blob/9d66af7141782f3f89655e6868abe3f141c97632/src/CommandApi.ts#L415)

Implements [ReadAfter](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafter) with the `ViewModels` of this process. View models that another process holds are not waited for, as with `ViewModels.caughtUp`.

#### Parameters

##### options?

[`ReadAfterOptions`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafteroptions)

#### Returns

`Layer`<[`ReadAfter`](https://effect-cqrs.dambox.ch/reference/api/commandapi/#readafter), `never`, [`ViewModels`](https://effect-cqrs.dambox.ch/reference/api/viewmodel/#viewmodels)>
