doxa-macros
Procedural macros for the doxa ecosystem. Derive macros, HTTP method attributes, and a capability declaration macro — all designed to eliminate boilerplate when building OpenAPI-documented axum services.
Most users should depend on doxa with the macros feature (enabled by default) rather than pulling this crate directly.
Derive macros
#[derive(ApiError)]
Turns an error enum into three trait implementations from a single #[api(...)] annotation per variant:
axum::response::IntoResponse— maps each variant to its HTTP status code and emits a typedApiErrorBody<Self>JSON envelope withmessage,status,code, anderrorfields.utoipa::IntoResponses— produces an OpenAPI response map. Variants sharing a status code are grouped into one response with per-variant named examples and aoneOfschema for theerrorfield.HasAuditOutcome— maps each variant to an audit outcome (allowed,denied, orerror) for automatic audit trail integration viaAuditLayer.
The generated IntoResponse also emits structured tracing: error! for 5xx, warn! for 4xx, debug! for everything else.
#[api(...)] attributes
| Key | Required | Description |
|---|---|---|
status |
yes | HTTP status code (u16) |
code |
no | Application error code string; defaults to snake_case of variant name |
outcome |
no | Audit outcome: "allowed", "denied", or "error" (default) |
#[derive(SseEvent)]
Implements SseEventMeta for internally-tagged enums so Server-Sent Event frames carry the variant name as the SSE event type. Pair with #[serde(tag = "event", content = "data", rename_all = "snake_case")] to keep wire format and OpenAPI schema aligned.
HTTP method attribute macros
#[get], #[post], #[put], #[patch], #[delete] delegate to utoipa::path with automatic inference from the handler's function signature. Use #[operation] for custom or multi-method routes.
async
async
What the macros infer
The method macros read the handler signature and automatically populate utoipa::path attributes:
| Inference | How it works |
|---|---|
operation_id |
Defaults to the function name |
request_body |
Detected from the first Json<T> parameter, including through transparent wrappers like Valid<Json<T>> |
| Path parameters | {name} segments in the route are matched to Path<T> extractors (scalar, tuple, struct) |
| Query parameters | Query<T> extractors contribute query parameters via trait dispatch |
| Header parameters | Header<H> extractors contribute header parameters; headers(H1, H2) documents headers without extracting |
| Success response | Json<T> → 200; (StatusCode, Json<T>) → 201; SseStream<E, _> → text/event-stream |
| Error responses | E from Result<_, E> folded into responses(...) as IntoResponses |
| Tags | tag = "Name" for one, tags("A", "B") for multiple |
Explicit overrides always win. Supplying request_body = ..., params(...), or responses(...) by hand suppresses inference for that field. Any additional key = value pairs are forwarded to utoipa::path verbatim.
Header documentation
Two equivalent ways to declare a header on a handler — both use the DocumentedHeader trait and deduplicate:
// Via extractor — extracts the value AND documents it
async
// Via attribute — documents without extracting
async
Capability attribute macro
#[capability] declares a Capable marker type backed by a Capability constant, for use with doxa_auth::Require<M>. Requires doxa-policy in the consumer's dependency tree.
use capability;
;
// Use in a handler — enforces at runtime AND stamps OpenAPI security metadata
async
Multiple checks(...) blocks are supported — all must pass for the capability to be granted.
#[capability] attributes
| Key | Required | Description |
|---|---|---|
name |
yes | Stable client-facing capability identifier (e.g. "widgets.read") |
description |
yes | Human-readable description, displayed in UI badges |
checks(...) |
yes (1+) | One or more check blocks with action, entity_type, entity_id |
License
Apache 2.0