> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK - Async migration

> Moving from the blocking v0.10.x client to the async v0.11.0-beta.2 client, one line per break.

v0.11.0-beta.2 makes the client async. Every break has a one-line fix.

## The changes

| Before (v0.10.x)                                                      | Now                                                                          |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `NexusClient::create(config)?`                                        | `NexusClient::create(config).await?`                                         |
| `client.sync()?`                                                      | `client.sync().await?`                                                       |
| `client.set_secret(k, v)?`                                            | `client.set_secret(k, v).await?`                                             |
| `client.delete_secret(k)?`                                            | `client.delete_secret(k).await?`                                             |
| `client.delete_secret_version(k, n)?`                                 | `client.delete_secret_version(k, n).await?`                                  |
| `client.evaluate_ab(..)?`                                             | `client.evaluate_ab(..).await?`                                              |
| `client.run_stream();`                                                | unchanged - it now returns a `StreamHandle` you can ignore or `abort()`      |
| `NexusConfig { api_key, endpoint, ttl, wif, sse_reconnect_cooldown }` | `NexusConfig { api_key, endpoint, ..Default::default() }`                    |
| `Err(NexusError::Http(e))`                                            | `Err(NexusError::Transport(e))`                                              |
| `spawn_blocking(move \|\| client.get_secret("K"))`                    | `client.get_secret("K")` - drop the wrapper                                  |
| exhaustive `match` on `NexusError`                                    | add a `_` arm; the enum is `#[non_exhaustive]`                               |
| `use westyx_nexus::sync_types::...`                                   | the wire types are private now; use `get_config` / `get_secret` / `get_flag` |

Cache reads are **unchanged and still synchronous**: `get_config`, `get_all_configs`, `get_secret`, `get_secret_file_path`, `get_flag`, `find_flag`, `get_all_flags`, `kind`, `key_type`, `billing_overdue`, `synced_at`. They read in-memory state and make no network call, so an `.await` would add noise without adding a suspension point.

## Before and after

```rust theme={null}
// v0.10.x
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = NexusClient::create(NexusConfig {
        api_key: std::env::var("NEXUS_API_KEY")?,
        endpoint: std::env::var("NEXUS_ENDPOINT")?,
        ttl: None,
        wif: None,
        sse_reconnect_cooldown: None,
    })?;
    client.run_stream();
    let secret = client.get_secret("DB_PASSWORD")?;
    let _ = secret;
    Ok(())
}
```

```rust theme={null}
// v0.11.0-beta.2
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = NexusClient::create(NexusConfig {
        api_key: std::env::var("NEXUS_API_KEY")?,
        endpoint: std::env::var("NEXUS_ENDPOINT")?,
        ..Default::default()
    })
    .await?;
    client.run_stream();
    let secret = client.get_secret("DB_PASSWORD")?; // still synchronous
    let _ = secret;
    Ok(())
}
```

## Staying synchronous

If your application has no runtime, enable the `blocking` feature and change one import:

```toml theme={null}
westyx-nexus = { version = "0.11.0-beta.2", features = ["blocking"] }
```

```rust theme={null}
// before
use westyx_nexus::NexusClient;
// after
use westyx_nexus::blocking::NexusClient;
```

Everything else stays as it was - the blocking client exposes the same methods without `.await`, and it owns a small runtime on its own threads so it works from inside another runtime too.

## Configuration errors moved

Validation failures used to arrive as `NexusError::SyncFailed(String)`, so telling an unparseable endpoint from a failed sync meant matching on message text. They are now `NexusError::Config(String)`, and the message names the option and, for lists, the index:

```text theme={null}
invalid configuration: `sse_reconnect_cooldown[1]` is 0 minutes - a zero cooldown
reconnects in a tight loop; the minimum is 1
```

Two values that used to be accepted are now rejected at construction: a `ttl` below one second, and a `0` entry in `sse_reconnect_cooldown`.

## Recovering the transport error

```rust theme={null}
use std::error::Error;
use westyx_nexus::NexusError;

if let Err(err) = client.sync().await {
    if matches!(err, NexusError::Transport(_)) {
        if let Some(source) = err.source() {
            eprintln!("transport failure: {source}");
            if let Some(e) = source.downcast_ref::<reqwest::Error>() {
                eprintln!("timeout: {}", e.is_timeout());
            }
        }
    }
}
```

The concrete type is deliberately absent from the signature, so a future change of HTTP client will not break your build again.

## Minimum Rust version

**1.88.** Raising it can break consumers on older toolchains, so it is stated explicitly and verified by a CI job that builds and tests with exactly that toolchain.

## New in the same release

* `close()` - stop all background work and remove materialised file-type secrets, at a point you choose.
* `synced_at()` - when the cache was last filled successfully.
* `find_flag()` - `None` for an absent flag, so you can tell it from one that is present and false.
* `StreamHandle::abort()` - stop SSE and keep TTL polling.
