{"id":27765720,"url":"https://github.com/thenativeweb/eventsourcingdb-client-javascript","last_synced_at":"2025-04-29T16:58:55.995Z","repository":{"id":285935693,"uuid":"491757603","full_name":"thenativeweb/eventsourcingdb-client-javascript","owner":"thenativeweb","description":"The official JavaScript client SDK for EventSourcingDB.","archived":false,"fork":false,"pushed_at":"2025-04-28T04:49:42.000Z","size":600,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-29T16:58:47.856Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.eventsourcingdb.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thenativeweb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-13T04:36:04.000Z","updated_at":"2025-04-28T04:49:45.000Z","dependencies_parsed_at":"2025-04-10T13:22:32.781Z","dependency_job_id":null,"html_url":"https://github.com/thenativeweb/eventsourcingdb-client-javascript","commit_stats":null,"previous_names":["thenativeweb/eventsourcingdb-client-javascript"],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-javascript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-javascript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-javascript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-javascript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thenativeweb","download_url":"https://codeload.github.com/thenativeweb/eventsourcingdb-client-javascript/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251546700,"owners_count":21606889,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-04-29T16:58:53.714Z","updated_at":"2025-04-29T16:58:55.990Z","avatar_url":"https://github.com/thenativeweb.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eventsourcingdb\n\nThe official JavaScript client SDK for [EventSourcingDB](https://www.eventsourcingdb.io) – a purpose-built database for event sourcing.\n\nEventSourcingDB enables you to build and operate event-driven applications with native support for writing, reading, and observing events. This client SDK provides convenient access to its capabilities in JavaScript and TypeScript.\n\nFor more information on EventSourcingDB, see its [official documentation](https://docs.eventsourcingdb.io/).\n\nThis client SDK includes support for [Testcontainers](https://testcontainers.com/) to spin up EventSourcingDB instances in integration tests. For details, see [Using Testcontainers](#using-testcontainers).\n\n## Getting Started\n\nInstall the client SDK:\n\n```shell\nnpm install eventsourcingdb\n```\n\nImport the `Client` class and create an instance by providing the URL of your EventSourcingDB instance and the API token to use:\n\n```typescript\nimport { Client } from 'eventsourcingdb';\n\nconst url = new URL('http://localhost:3000');\nconst apiToken = 'secret';\n\nconst client = new Client(url, apiToken);\n```\n\nThen call the `ping` function to check whether the instance is reachable. If it is not, the function will throw an error:\n\n```typescript\nawait client.ping();\n```\n\n*Note that `ping` does not require authentication, so the call may succeed even if the API token is invalid.*\n\nIf you want to verify the API token, call `verifyApiToken`. If the token is invalid, the function will throw an error:\n\n```typescript\nawait client.verifyApiToken();\n```\n\n### Writing Events\n\nCall the `writeEvents` function and hand over an array with one or more events. You do not have to provide all event fields – some are automatically added by the server.\n\nSpecify `source`, `subject`, `type`, and `data` according to the [CloudEvents](https://docs.eventsourcingdb.io/fundamentals/cloud-events/) format.\n\nThe function returns the written events, including the fields added by the server:\n\n```typescript\nconst writtenEvents = await client.writeEvents([\n  {\n    source: 'https://library.eventsourcingdb.io',\n    subject: '/books/42',\n    type: 'io.eventsourcingdb.library.book-acquired',\n    data: {\n      title: '2001 – A Space Odyssey',\n      author: 'Arthur C. Clarke',\n      isbn: '978-0756906788'\n    }\n  }\n]);\n```\n\n#### Using the `isSubjectPristine` precondition\n\nIf you only want to write events in case a subject (such as `/books/42`) does not yet have any events, import the `isSubjectPristine` function and pass it as the second argument as an array of preconditions:\n\n```typescript\nimport { isSubjectPristine } from 'eventsourcingdb';\n\nconst writtenEvents = await client.writeEvents([\n  // events\n], [\n  isSubjectPristine('/books/42')\n]);\n```\n\n#### Using the `isSubjectOnEventId` precondition\n\nIf you only want to write events in case the last event of a subject (such as `/books/42`) has a specific ID (e.g., `0`), import the `isSubjectOnEventId` function and pass it as an array of preconditions in the second argument:\n\n```typescript\nimport { isSubjectOnEventId } from 'eventsourcingdb';\n\nconst writtenEvents = await client.writeEvents([\n  // events\n], [\n  isSubjectOnEventId('/books/42', '0')\n]);\n```\n\n*Note that according to the CloudEvents standard, event IDs must be of type string.*\n\n### Reading Events\n\nTo read all events of a subject, call the `readEvents` function with the subject as the first argument and an options object as the second argument. Set the `recursive` option to `false`. This ensures that only events of the given subject are returned, not events of nested subjects.\n\nThe function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop:\n\n```typescript\nfor await (const event of client.readEvents('/books/42', {\n  recursive: false\n})) {\n  // ...\n}\n```\n\n#### Reading From Subjects Recursively\n\nIf you want to read not only all the events of a subject, but also the events of all nested subjects, set the `recursive` option to `true`:\n\n```typescript\nfor await (const event of client.readEvents('/books/42', {\n  recursive: true\n})) {\n  // ...\n}\n```\n\nThis also allows you to read *all* events ever written. To do so, provide `/` as the subject and set `recursive` to `true`, since all subjects are nested under the root subject.\n\n#### Reading in Anti-Chronological Order\n\nBy default, events are read in chronological order. To read in anti-chronological order, provide the `order` option and set it to `antichronological`:\n\n```typescript\nfor await (const event of client.readEvents('/books/42', {\n  recursive: false,\n  order: 'antichronological'\n})) {\n  // ...\n}\n```\n\n*Note that you can also specify `chronological` to explicitly enforce the default order.*\n\n#### Specifying Bounds\n\nSometimes you do not want to read all events, but only a range of events. For that, you can specify the `lowerBound` and `upperBound` options – either one of them or even both at the same time.\n\nSpecify the ID and whether to include or exclude it, for both the lower and upper bound:\n\n```typescript\nfor await (const event of client.readEvents('/books/42', {\n  recursive: false,\n  lowerBound: { id: '100', type: 'inclusive' },\n  upperBound: { id: '200', type: 'exclusive' }\n})) {\n  // ...\n}\n```\n\n#### Starting From the Latest Event of a Given Type\n\nTo read starting from the latest event of a given type, provide the `fromLatestEvent` option and specify the subject, the type, and how to proceed if no such event exists.\n\nPossible options are `read-nothing`, which skips reading entirely, or `read-everything`, which effectively behaves as if `fromLatestEvent` was not specified:\n\n```typescript\nfor await (const event of client.readEvents('/books/42', {\n  recursive: false,\n  fromLatestEvent: {\n    subject: '/books/42',\n    type: 'io.eventsourcingdb.library.book-borrowed',\n    ifEventIsMissing: 'read-everything'\n  }\n})) {\n  // ...\n}\n```\n\n*Note that `fromLatestEvent` and `lowerBound` can not be provided at the same time.*\n\n#### Aborting Reading\n\nIf you need to abort reading use `break` or `return` within the `for await` loop. However, this only works if there is currently an iteration going on.\n\nTo abort reading independently of that, hand over an abort signal as third argument when calling `readEvents`, and abort the appropriate `AbortController`:\n\n```typescript\nconst controller = new AbortController();\n\nfor await (const event of client.readEvents('/books/42', {\n  recursive: false\n}, controller.signal)) {\n  // ...\n}\n\n// Somewhere else, abort the controller, which will cause\n// reading to end.\ncontroller.abort();\n```\n\n### Running EventQL Queries\n\nTo run an EventQL query, call the `runEventQlQuery` function and provide the query as argument. The function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop:\n\n```typescript\nfor await (const row of client.runEventQlQuery(`\n  FROM e IN events\n  PROJECT INTO e\n`)) {\n  // ...\n}\n```\n\n*Note that each row returned by the iterator matches the projection specified in your query.*\n\n#### Aborting a Query\n\nIf you need to abort a query use `break` or `return` within the `for await` loop. However, this only works if there is currently an iteration going on.\n\nTo abort the query independently of that, hand over an abort signal as second argument when calling `runEventQlQuery`, and abort the appropriate AbortController:\n\n```typescript\nconst controller = new AbortController();\n\nfor await (const row of client.runEventQlQuery(`\n  FROM e IN events\n  PROJECT INTO e\n`, controller.signal)) {\n  // ...\n}\n\n// Somewhere else, abort the controller, which will cause\n// the query to end.\ncontroller.abort();\n```\n\n### Observing Events\n\nTo observe all events of a subject, call the `observeEvents` function with the subject as the first argument and an options object as the second argument. Set the `recursive` option to `false`. This ensures that only events of the given subject are returned, not events of nested subjects.\n\nThe function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop:\n\n```typescript\nfor await (const event of client.observeEvents('/books/42', {\n  recursive: false\n})) {\n  // ...\n}\n```\n\n#### Observing From Subjects Recursively\n\nIf you want to observe not only all the events of a subject, but also the events of all nested subjects, set the `recursive` option to `true`:\n\n```typescript\nfor await (const event of client.observeEvents('/books/42', {\n  recursive: true\n})) {\n  // ...\n}\n```\n\nThis also allows you to observe *all* events ever written. To do so, provide `/` as the subject and set `recursive` to `true`, since all subjects are nested under the root subject.\n\n#### Specifying Bounds\n\nSometimes you do not want to observe all events, but only a range of events. For that, you can specify the `lowerBound` option.\n\nSpecify the ID and whether to include or exclude it:\n\n```typescript\nfor await (const event of client.observeEvents('/books/42', {\n  recursive: false,\n  lowerBound: { id: '100', type: 'inclusive' }\n})) {\n  // ...\n}\n```\n\n#### Starting From the Latest Event of a Given Type\n\nTo observe starting from the latest event of a given type, provide the `fromLatestEvent` option and specify the subject, the type, and how to proceed if no such event exists.\n\nPossible options are `wait-for-event`, which waits for an event of the given type to happen, or `read-everything`, which effectively behaves as if `fromLatestEvent` was not specified:\n\n```typescript\nfor await (const event of client.observeEvents('/books/42', {\n  recursive: false,\n  fromLatestEvent: {\n    subject: '/books/42',\n    type: 'io.eventsourcingdb.library.book-borrowed',\n    ifEventIsMissing: 'read-everything'\n  }\n})) {\n  // ...\n}\n```\n\n*Note that `fromLatestEvent` and `lowerBound` can not be provided at the same time.*\n\n#### Aborting Observing\n\nIf you need to abort observing use `break` or `return` within the `for await` loop. However, this only works if there is currently an iteration going on.\n\nTo abort observing independently of that, hand over an abort signal as third argument when calling `observeEvents`, and abort the appropriate `AbortController`:\n\n```typescript\nconst controller = new AbortController();\n\nfor await (const event of client.observeEvents('/books/42', {\n  recursive: false\n}, controller.signal)) {\n  // ...\n}\n\n// Somewhere else, abort the controller, which will cause\n// observing to end.\ncontroller.abort();\n```\n\n### Registering an Event Schema\n\nTo register an event schema, call the `registerEventSchema` function and hand over an event type and the desired schema:\n\n```typescript\nawait client.registerEventSchema('io.eventsourcingdb.library.book-acquired', {\n  type: 'object',\n  properties: {\n    title: { type: 'string' },\n    author: { type: 'string' },\n    isbn: { type: 'string' }\n  },\n  required: [\n    'title',\n    'author',\n    'isbn'\n  ],\n  additionalProperties: false\n});\n```\n\n### Listing Subjects\n\nTo list all subjects, call the `readSubjects` function with `/` as the base subject. The function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop:\n\n```typescript\nfor await (const subject of client.readSubjects('/')) {\n  // ...\n}\n```\n\nIf you only want to list subjects within a specific branch, provide the desired base subject instead:\n\n```typescript\nfor await (const subject of client.readSubjects('/books')) {\n  // ...\n}\n```\n\n#### Aborting Listing\n\nIf you need to abort listing use `break` or `return` within the `for await` loop. However, this only works if there is currently an iteration going on.\n\nTo abort listing independently of that, hand over an abort signal as second argument when calling `readSubjects`, and abort the appropriate `AbortController`:\n\n```typescript\nconst controller = new AbortController();\n\nfor await (const subject of client.readSubjects(\n  '/', controller.signal)\n) {\n  // ...\n}\n\n// Somewhere else, abort the controller, which will cause\n// reading to end.\ncontroller.abort();\n```\n\n### Listing Event Types\n\nTo list all event types, call the `readEventTypes` function. The function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop:\n\n```typescript\nfor await (const eventType of client.readEventTypes()) {\n  // ...\n}\n```\n\n#### Aborting Listing\n\nIf you need to abort listing use `break` or `return` within the `for await` loop. However, this only works if there is currently an iteration going on.\n\nTo abort listing independently of that, hand over an abort signal as argument when calling `readEventTypes`, and abort the appropriate `AbortController`:\n\n```typescript\nconst controller = new AbortController();\n\nfor await (const eventType of client.readEventTypes()) {\n  // ...\n}\n\n// Somewhere else, abort the controller, which will cause\n// reading to end.\ncontroller.abort();\n```\n\n### Using Testcontainers\n\nImport the `EventSourcingDbContainer` class, create an instance, call the `start` function to run a test container, get a client, run your test code, and finally call the `stop` function to stop the test container:\n\n```typescript\nimport { EventSourcingDbContainer } from 'eventsourcingdb';\n\nconst container = new EventSourcingDbContainer();\nawait container.start();\n\nconst client = container.getClient();\n\n// ...\n\nawait container.stop();\n```\n\nTo check if the test container is running, call the `isRunning` function:\n\n```typescript\nconst isRunning = container.isRunning();\n```\n\n#### Configuring the Container Instance\n\nBy default, `EventSourcingDbContainer` uses the `latest` tag of the official EventSourcingDB Docker image. To change that, call the `withImageTag` function:\n\n```typescript\nconst container = new EventSourcingDbContainer()\n  .withImageTag('1.0.0');\n```\n\nSimilarly, you can configure the port to use and the API token. Call the `withPort` or the `withApiToken` function respectively:\n\n```typescript\nconst container = new EventSourcingDbContainer()\n  .withPort(4000)\n  .withApiToken('secret');\n```\n\n#### Configuring the Client Manually\n\nIn case you need to set up the client yourself, use the following functions to get details on the container:\n\n- `getHost()` returns the host name\n- `getMappedPort()` returns the port\n- `getBaseUrl()` returns the full URL of the container\n- `getApiToken()` returns the API token\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthenativeweb%2Feventsourcingdb-client-javascript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthenativeweb%2Feventsourcingdb-client-javascript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthenativeweb%2Feventsourcingdb-client-javascript/lists"}