# AsyncAPI
With the `asyncapi` feature, RustStream generates an [AsyncAPI 3.0](https://www.asyncapi.com/)
document from the application's handlers: each subscriber becomes a channel and a `receive`
operation, and payload types contribute schemas.
```toml
ruststream = { version = "0.6", features = ["macros", "memory", "asyncapi"] }
```
## Generating the document
The quickest path is the CLI, which runs your service's generator and prints the document:
```bash
ruststream asyncapi gen # JSON to stdout
ruststream asyncapi gen -o asyncapi.json
ruststream asyncapi gen --yaml
```
In code, build the spec from the application with `build_spec`, then serialize it with `to_json` or
`to_yaml`:
```rust
--8<-- "examples/asyncapi_http.rs:generate"
```
`#[ruststream::app]` wires the `asyncapi gen` command to `build_spec` for you, so the CLI and a
hand-written call produce the same document.
## Payload schemas
A handler's payload type appears as a schema when it derives `JsonSchema`. RustStream re-exports
`schemars`, so you do not need a direct dependency:
```rust
--8<-- "examples/asyncapi_http.rs:payload"
```
A type without `JsonSchema` still works as a handler payload; it just contributes no schema to the
document.
## Message names and descriptions
A documented payload type feeds the message component on its own: with the `JsonSchema` derive,
the type's doc comment becomes the message description, and a `#[schemars(title = "...")]` (or
rename) names the component. Without a schema, the component is named after the payload type and
the description falls back to the handler's doc comment (which also documents the `receive`
operation).
To control the metadata explicitly - including for types without `JsonSchema` - implement the
`Message` trait, which takes precedence over the schema; or derive it, which uses the type's name
and doc comment:
```rust
use ruststream::Message;
/// An order placed by a customer.
#[derive(Message, serde::Deserialize)]
struct Order {
id: u64,
}
// In the document: components.messages.Order with that description.
```
A manual `impl Message` can name the component differently from the Rust type
(`const NAME: &'static str = "CustomOrder";`), which keeps the wire contract stable across renames.
## Servers
Record the servers your service connects to so they appear in the document's `servers` section.
Build a `ServerSpec` directly:
```rust
--8<-- "examples/asyncapi_http.rs:server"
```
A broker crate may also implement the `DescribeServer` capability, in which case
`broker.describe_server()` produces the spec for you (the shipped brokers all do), and
`with_broker_labeled` records it automatically under the broker's label.
## Server security
Declare how clients authenticate with `ServerSpec::with_security`; each scheme lands in
`components.securitySchemes` and is referenced from the server's `security` list:
```rust
--8<-- "examples/asyncapi_http.rs:security"
```
`SecurityScheme` has constructors for the AsyncAPI 3.0 scheme kinds - `user_password`, `plain`,
`scram_sha256` / `scram_sha512`, `gssapi`, `api_key`, `x509`, `http`, `http_api_key`,
`open_id_connect`, and `oauth2` (which takes the flows object as raw JSON) - plus
`SecurityScheme::custom(json)` as the escape hatch for anything they do not model. Without
`with_security` the document carries no security sections at all.
Security is deliberately the service author's statement, not the broker's: the same broker is
deployed publicly and internally with different authentication, so `DescribeServer` never reports
it. To secure a server a broker registered automatically (`with_broker_labeled`), declare it
explicitly instead: `.server(label, broker.describe_server().with_security(..))` with the same
label.
## Serving the document
Hosting is intentionally not part of the framework. `build_spec` and `to_json` / `to_yaml` give you
the bytes; you mount them in whatever HTTP stack you already run (axum, actix, or any other).
For an interactive viewer, `render_viewer_html` returns a self-contained HTML page that loads the
AsyncAPI React component and points it at your spec URL:
```rust
use ruststream::asyncapi::{render_viewer_html, ViewerOptions};
let html = render_viewer_html("/asyncapi.json", &ViewerOptions::default());
```
Serve that HTML and the spec JSON from two routes in your own server. By default the viewer loads its
assets from a CDN; override the base URL with `ViewerOptions::with_cdn_base` for offline or
locked-down deployments (`with_title` sets the page title).
## A complete server
The [`asyncapi_http`](https://github.com/powersemmi/ruststream/blob/main/examples/asyncapi_http.rs)
example serves the document and the viewer with [axum](https://github.com/tokio-rs/axum). Run it with
`cargo run --example asyncapi_http --features macros,memory,asyncapi`, then open
<http://127.0.0.1:8080/>.
```rust
--8<-- "examples/asyncapi_http.rs"
```