{"id":30830553,"url":"https://github.com/thenativeweb/eventsourcingdb-client-rust","last_synced_at":"2025-09-06T15:55:24.610Z","repository":{"id":298309075,"uuid":"977966362","full_name":"thenativeweb/eventsourcingdb-client-rust","owner":"thenativeweb","description":"The official Rust client SDK for EventSourcingDB.","archived":false,"fork":false,"pushed_at":"2025-08-25T07:33:41.000Z","size":372,"stargazers_count":4,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-25T09:26:52.195Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.eventsourcingdb.io","language":"Rust","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":"2025-05-05T09:04:54.000Z","updated_at":"2025-08-25T07:33:44.000Z","dependencies_parsed_at":"2025-06-10T13:04:40.667Z","dependency_job_id":"6ab6b4ee-7489-4b21-a1da-81441650810d","html_url":"https://github.com/thenativeweb/eventsourcingdb-client-rust","commit_stats":null,"previous_names":["thenativeweb/eventsourcingdb-client-rust"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/thenativeweb/eventsourcingdb-client-rust","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thenativeweb","download_url":"https://codeload.github.com/thenativeweb/eventsourcingdb-client-rust/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thenativeweb%2Feventsourcingdb-client-rust/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273926545,"owners_count":25192318,"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","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-09-06T15:55:13.801Z","updated_at":"2025-09-06T15:55:24.596Z","avatar_url":"https://github.com/thenativeweb.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eventsourcingdb\n\nThe official Rust 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 Rust.\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\ncargo add eventsourcingdb\n```\n\nImport the package and create an instance by providing the URL of your EventSourcingDB instance and the API token to use:\n\n```rust\nuse eventsourcingdb::client::Client;\n\n// ...\n\nlet base_url: Url = \"localhost:3000\".parse().unwrap();\nlet api_token = \"secret\";\nlet client = Client::new(base_url, api_token);\n```\n\nThen call the `ping` function to check whether the instance is reachable. If it is not, the function will return an error:\n\n```rust\nlet result = client.ping().await;\nif let Err(err) = result {\n  // handle error...\n}\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 `verify_api_token`. If the token is invalid, the function will return an error:\n\n```rust\nlet result = client.verify_api_token().await;\nif let Err(err) = result {\n  // handle error...\n}\n```\n\n### Writing Events\n\nCall the `write_events` function and hand over a vector 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` (using `ty`), and `data` according to the [CloudEvents](https://docs.eventsourcingdb.io/fundamentals/cloud-events/) format.\n\nFor `data` provide a JSON object using a `serde_json:Value`.\n\nThe function returns the written events, including the fields added by the server:\n\n```rust\nlet event = EventCandidate::builder()\n  .source(\"https://library.eventsourcingdb.io\".to_string())\n  .subject(\"/books/42\".to_string())\n  .ty(\"io.eventsourcingdb.library.book-acquired\")\n  .data(json!({\n    \"title\": \"2001 - A Space Odyssey\",\n    \"author\": \"Arthur C. Clarke\",\n    \"isbn\": \"978-0756906788\",\n  }))\n  .build();\n\nlet result = client.write_events(vec![event.clone()], vec![]).await;\nmatch result {\n  Ok(written_events) =\u003e // ...\n  Err(err) =\u003e // ...\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, use the `IsSubjectPristine` precondition to create a precondition and pass it in a vector as the second argument:\n\n```rust\nlet result = client.write_events(\n  vec![event.clone()],\n  vec![Precondition::IsSubjectPristine {\n    subject: \"/books/42\".to_string(),\n  }],\n).await;\nmatch result {\n  Ok(written_events) =\u003e // ...\n  Err(err) =\u003e // ...\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`), use the `IsSubjectOnEventId` precondition to create a precondition and pass it in a vector as the second argument:\n\n```rust\nlet result = client.write_events(\n  vec![event.clone()],\n  vec![Precondition::IsSubjectOnEventId {\n    subject: \"/books/42\".to_string(),\n    event_id: \"0\".to_string(),\n  }],\n).await;\nmatch result {\n  Ok(written_events) =\u003e // ...\n  Err(err) =\u003e // ...\n}\n```\n\n*Note that according to the CloudEvents standard, event IDs must be of type string.*\n\n#### Using the `IsEventQLQueryTrue` precondition\n\nIf you want to write events depending on an EventQL query, use the `IsEventQLQueryTrue` precondition to create a precondition and pass it in a vector as the second argument:\n\n```rust\nlet result = client.write_events(\n  vec![event.clone()],\n  vec![Precondition::IsEventQLQueryTrue {\n    query: \"FROM e IN events WHERE e.type == 'io.eventsourcingdb.library.book-borrowed' PROJECT INTO COUNT() \u003c 10\".to_string(),\n  }],\n).await;\nmatch result {\n  Ok(written_events) =\u003e // ...\n  Err(err) =\u003e // ...\n}\n```\n\n### Reading Events\n\nTo read all events of a subject, call the `read_events` function with the subject and an options object. 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 a stream from which you can retrieve one event at a time:\n\n```rust\nlet result = client\n  .read_events(\"/books/42\", Some(\n    ReadEventsOptions {\n      recursive: false,\n      ...Default::default(),\n    }\n  ))\n  .await;\n\nmatch result {\n  Err(err) =\u003e // ...\n  Ok(mut stream) =\u003e {\n    while let Some(event) = stream.next().await {\n      // ...\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```rust\nlet result = client\n  .read_events(\"/books/42\", Some(\n    ReadEventsOptions {\n      recursive: true,\n      ..Default::default(),\n    }\n  ))\n  .await;\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 using the `Antichronological` ordering:\n\n```rust\nlet result = client\n  .read_events(\"/books/42\", Some(\n    ReadEventsOptions {\n      recursive: false,\n      order: Some(Ordering::Antichronological)\n      ..Default::default(),\n    }\n  ))\n  .await;\n```\n\n*Note that you can also use the `Chronological` ordering 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 `lower_bound` and `upper_bound` 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```rust\nlet result = client\n  .read_events(\"/books/42\", Some(\n    ReadEventsOptions {\n      recursive: false,\n      lower_bound: Some(Bound {\n        bound_type: BoundType::Inclusive,\n        id: \"100\",\n      }),\n      upper_bound: Some(Bound {\n        bound_type: BoundType::Exclusive,\n        id: \"200\",\n      }),\n      ..Default::default(),\n    }\n  ))\n  .await;\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 `from_latest_event` option and specify the subject, the type, and how to proceed if no such event exists.\n\nPossible options are `ReadNothing`, which skips reading entirely, or `ReadEverything`, which effectively behaves as if `from_latest_event` was not specified:\n\n```rust\nlet result = client\n  .read_events(\"/books/42\", Some(\n    ReadEventsOptions {\n      recursive: false,\n      from_latest_event: Some(\n        FromLatestEventOptions {\n          subject: \"/books/42\",\n          ty: \"io.eventsourcingdb.library.book-borrowed\",\n          if_event_is_missing: ReadEventMissingStrategy::ReadEverything,\n        }\n      )\n      ..Default::default(),\n    }\n  ))\n  .await;\n```\n\n*Note that `from_latest_event` and `lower_bound` can not be provided at the same time.*\n\n### Running EventQL Queries\n\nTo run an EventQL query, call the `run_eventql_query` function and provide the query as argument. The function returns a stream.\n\n```rust\nlet result = client\n  .run_eventql_query(\"FROM e IN events PROJECT INTO e\")\n  .await;\n\nmatch result {\n  Err(err) =\u003e // ...\n  Ok(mut stream) =\u003e {\n    while let Some(row) = stream.next().await {\n      // ...\n    }\n  }\n}\n```\n\n*Note that each row returned by the stream is of type `serde_json::Value` and matches the projection specified in your query.*\n\n### Observing Events\n\nTo observe all events of a subject, call the `observe_events` function with the subject and an options object. 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 a stream from which you can retrieve one event at a time:\n\n```rust\nlet result = client\n  .observe_events(\"/books/42\", Some(\n    ObserveEventsOptions {\n      recursive: false,\n      from_latest_event: None,\n      lower_bound: None,\n    }\n  ))\n  .await;\n\nmatch result {\n  Err(err) =\u003e // ...\n  Ok(mut stream) =\u003e {\n    while let Some(event) = stream.next().await {\n      // ...\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```rust\nlet result = client\n  .observe_events(\"/books/42\", Some(\n    ObserveEventsOptions {\n      recursive: true,\n      ..Default::default(),\n    }\n  ))\n  .await\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 `lower_bound` option.\n\nSpecify the ID and whether to include or exclude it:\n\n```rust\nlet result = client\n  .observe_events(\"/books/42\", Some(\n    ObserveEventsOptions {\n      recursive: false,\n      lower_bound: Some(Bound {\n        bound_type: BoundType::Inclusive,\n        id: \"100\",\n      }),\n      ..Default::default(),\n    }\n  ))\n  .await\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 `from_latest_event` option and specify the subject, the type, and how to proceed if no such event exists.\n\nPossible options are `WaitForEvent`, which waits for an event of the given type to happen, or `ObserveEverything`, which effectively behaves as if `from_latest_event` was not specified:\n\n```rust\nlet result = client\n  .observe_events(\"/books/42\", Some(\n    ObserveEventsOptions {\n      recursive: false,\n      from_latest_event: Some(\n        ObserveFromLatestEventOptions {\n          subject: \"/books/42\",\n          ty: \"io.eventsourcingdb.library.book-borrowed\",\n          if_event_is_missing: ObserveEventMissingStrategy::ObserveEverything,\n        }\n      )\n      ..Default::default(),\n    }\n  ))\n  .await\n```\n\n*Note that `from_latest_event` and `lower_bound` can not be provided at the same time.*\n\n#### Aborting Observing\n\nThe observe will automatically be canceled if the stream is dropped from scope.\n\n### Registering an Event Schema\n\nTo register an event schema, call the `register_event_schema` function and hand over an event type and the desired schema:\n\n```rust\nclient.register_event_schema(\n  \"io.eventsourcingdb.library.book-acquired\",\n  \u0026json!({\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  }).await;,\n)\n```\n\n### Listing Subjects\n\nTo list all subjects, call the `list_subjects` function with `/` as the base subject. The function returns a stream from which you can retrieve one subject at a time:\n\n```rust\nlet result = client.list_subjects(Some(\"/\")).await;\nmatch result {\n  Ok(subjects) =\u003e // ...\n  Err(err) =\u003e // ...\n}\n```\n\nIf you only want to list subjects within a specific branch, provide the desired base subject instead:\n\n```rust\nlet result := client.list_subjects(\"/books\");\n```\n\n### Listing Event Types\n\nTo list all event types, call the `list_event_types` function. The function returns a stream from which you can retrieve one event type at a time:\n\n```rust\nlet result := client.list_event_types().await;\nmatch result {\n  Ok(event_types) =\u003e // ...\n  Err(err) =\u003e // ...\n}\n```\n\n### Listing a Specific Event Types\n\nTo list a specific event type, call the `read_event_type` function. The function returns the detailed event type, which includes the schema:\n\n```rust\nlet event_type_name = \"io.eventsourcingdb.library.book-acquired\";\nlet result := client.read_event_type(event_type_name).await;\nmatch result {\n  Ok(event_type) =\u003e // ...\n  Err(err) =\u003e // ...\n}\n```\n\n### Using Testcontainers\n\nCall the `Container::start_default()` function, get a client, and run your test code:\n\n```rust\nlet container  = Container::start_default().await.unwrap();\nlet client = container.get_client().await.unwrap();\n```\n\n#### Configuring the Container Instance\n\nBy default, `Container` uses the `latest` tag of the official EventSourcingDB Docker image. To change that use the provided builder and call the `with_image_tag` function.\n\n```rust\nlet container = Container::builder()\n  .with_image_tag(\"1.0.0\")\n  .build()\n  .await.unwrap()\n```\n\nSimilarly, you can configure the port to use and the API token. Call the `with_port` or the `with_api_token` function respectively:\n\n```rust\nlet container = Container::builder()\n  .with_port(4000)\n  .with_api_token(\"secret\")\n  .build()\n  .await.unwrap()\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- `get_host()` returns the host name\n- `get_mapped_port()` returns the port\n- `get_base_url()` returns the full URL of the container\n- `get_api_token()` returns the API token\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthenativeweb%2Feventsourcingdb-client-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthenativeweb%2Feventsourcingdb-client-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthenativeweb%2Feventsourcingdb-client-rust/lists"}