Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 5 additions & 54 deletions docs/api/appending-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,23 +205,12 @@ This feature is only available in KurrentDB 25.1 and later.

You can append events to multiple streams in a single atomic operation. Either all streams are updated, or the entire operation fails.

The `multiStreamAppend` method accepts a collection of `AppendStreamRequest` objects and returns a `MultiAppendResult`. Each `AppendStreamRequest` contains:

- **streamName** - The name of the stream
- **expectedState** - The expected state of the stream for optimistic concurrency control
- **events** - A collection of `EventData` objects to append

The operation returns either:
- `AppendStreamSuccess` - Successful append results for all streams
- `AppendStreamFailure` - Specific exceptions for any failed operations

::: warning
Event metadata in `EventData` must be valid JSON objects. This requirement will
be removed in a future major release.
Currently, metadata must be valid JSON. Binary metadata will not be supported in
this version. This limitation ensures compatibility with KurrentDB's metadata
handling and will be removed in the next major release.
:::

Here's a basic example of appending events to multiple streams:

```ts
import { jsonEvent } from "@kurrent/kurrentdb-client";
import { v4 as uuid } from "uuid";
Expand Down Expand Up @@ -265,43 +254,5 @@ const requests = [
}
];

const result = await client.multiStreamAppend(requests);

if (result.success) {
result.output.forEach((success) => {
console.log(`Stream '${success.streamName}' updated at position ${success.position}`);
});
}
```

If the operation doesn't succeed, it can fail with the following exceptions:

```ts
const result = await client.multiStreamAppend(requests);

if (!result.success) {
result.output.forEach((failure) => {
switch (failure.details.type) {
case "wrong_expected_revision":
console.log(`Version conflict in stream '${failure.streamName}': expected revision ${failure.details.revision}`);
break;

case "access_denied":
console.log(`Access denied to stream '${failure.streamName}': ${failure.details.reason}`);
break;

case "stream_deleted":
console.log(`Stream '${failure.streamName}' was deleted`);
break;

case "transaction_max_size_exceeded":
console.log(`Transaction too large for stream '${failure.streamName}': max size is ${failure.details.maxSize} bytes`);
break;

default:
console.log(`Unexpected error for stream '${failure.streamName}': ${failure.details.type}`);
break;
}
});
}
```
await client.multiStreamAppend(requests);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// GENERATED CODE -- NO SERVICES IN PROTO
105 changes: 105 additions & 0 deletions packages/db-client/generated/kurrentdb/protocols/v2/errors_pb.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// package: kurrent.rpc
// file: kurrentdb/protocols/v2/errors.proto

/* tslint:disable */
/* eslint-disable */

import * as jspb from "google-protobuf";
import * as kurrentdb_protocols_v2_rpc_pb from "../../../kurrentdb/protocols/v2/rpc_pb";

export class AccessDeniedErrorDetails extends jspb.Message {
getOperation(): string;
setOperation(value: string): AccessDeniedErrorDetails;

hasUsername(): boolean;
clearUsername(): void;
getUsername(): string | undefined;
setUsername(value: string): AccessDeniedErrorDetails;

hasPermission(): boolean;
clearPermission(): void;
getPermission(): string | undefined;
setPermission(value: string): AccessDeniedErrorDetails;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): AccessDeniedErrorDetails.AsObject;
static toObject(includeInstance: boolean, msg: AccessDeniedErrorDetails): AccessDeniedErrorDetails.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: AccessDeniedErrorDetails, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): AccessDeniedErrorDetails;
static deserializeBinaryFromReader(message: AccessDeniedErrorDetails, reader: jspb.BinaryReader): AccessDeniedErrorDetails;
}

export namespace AccessDeniedErrorDetails {
export type AsObject = {
operation: string,
username?: string,
permission?: string,
}
}

export class NotLeaderNodeErrorDetails extends jspb.Message {

hasCurrentLeader(): boolean;
clearCurrentLeader(): void;
getCurrentLeader(): NotLeaderNodeErrorDetails.NodeInfo | undefined;
setCurrentLeader(value?: NotLeaderNodeErrorDetails.NodeInfo): NotLeaderNodeErrorDetails;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NotLeaderNodeErrorDetails.AsObject;
static toObject(includeInstance: boolean, msg: NotLeaderNodeErrorDetails): NotLeaderNodeErrorDetails.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NotLeaderNodeErrorDetails, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NotLeaderNodeErrorDetails;
static deserializeBinaryFromReader(message: NotLeaderNodeErrorDetails, reader: jspb.BinaryReader): NotLeaderNodeErrorDetails;
}

export namespace NotLeaderNodeErrorDetails {
export type AsObject = {
currentLeader?: NotLeaderNodeErrorDetails.NodeInfo.AsObject,
}


export class NodeInfo extends jspb.Message {
getHost(): string;
setHost(value: string): NodeInfo;
getPort(): number;
setPort(value: number): NodeInfo;

hasNodeId(): boolean;
clearNodeId(): void;
getNodeId(): string | undefined;
setNodeId(value: string): NodeInfo;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NodeInfo.AsObject;
static toObject(includeInstance: boolean, msg: NodeInfo): NodeInfo.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NodeInfo, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NodeInfo;
static deserializeBinaryFromReader(message: NodeInfo, reader: jspb.BinaryReader): NodeInfo;
}

export namespace NodeInfo {
export type AsObject = {
host: string,
port: number,
nodeId?: string,
}
}

}

export enum ServerError {
UNSPECIFIED = 0,
SERVER_ERROR_ACCESS_DENIED = 1,
SERVER_ERROR_BAD_REQUEST = 2,
SERVER_ERROR_NOT_LEADER_NODE = 5,
SERVER_ERROR_OPERATION_TIMEOUT = 6,
SERVER_ERROR_SERVER_NOT_READY = 7,
SERVER_ERROR_SERVER_OVERLOADED = 8,
SERVER_ERROR_SERVER_MALFUNCTION = 9,
}
Loading
Loading