A Rust proc-macro attribute that reads an OpenAPI specification file at compile time and generates typed Rust traits from it, so you can implement your API server or define a transport-agnostic client contract with full type safety and no boilerplate.
#[openapi_trait::axum("openapi/petstore.yaml")]
pub mod petstore {}
use petstore::PetstoreApi as _;
#[derive(Clone)]
struct AppState;
#[derive(Clone)]
struct MyServer;
impl petstore::PetstoreApi<AppState> for MyServer {
type Error = std::convert::Infallible;
async fn get_pet_by_id(
&self,
req: petstore::GetPetByIdRequest,
_state: axum::extract::State<AppState>,
_headers: axum::http::HeaderMap,
) -> Result<petstore::GetPetByIdResponse, Self::Error> {
Ok(petstore::GetPetByIdResponse::Status200(petstore::Pet {
id: Some(req.pet_id),
name: "doggie".into(),
photo_urls: vec![],
category: None,
tags: None,
status: None,
}))
}
}
// Wire up an axum router.
let app = MyServer.router().with_state(AppState);#[openapi_trait::client("openapi/petstore.yaml")]
pub mod petstore {}
struct MyClient;
impl petstore::PetstoreClient for MyClient {
type Error = std::convert::Infallible;
async fn get_pet_by_id(
&self,
req: petstore::GetPetByIdRequest,
) -> Result<petstore::GetPetByIdResponse, Self::Error> {
Ok(petstore::GetPetByIdResponse::Status200(petstore::Pet {
id: Some(req.pet_id),
name: "doggie".into(),
photo_urls: vec![],
category: None,
tags: None,
status: None,
}))
}
}For every OpenAPI spec the macro emits inside the target module:
| Generated item | Source |
|---|---|
Structs with serde derives |
components/schemas |
{OperationId}Request structs |
Path, query, header params + request body per operation |
Per-operation {OperationId}Response enums |
HTTP status codes per operation |
impl axum::response::IntoResponse |
For every response enum generated by openapi_trait::axum |
{ModName}Api<S = ()> trait |
One method per operationId for server implementations |
{ModName}Client trait |
One method per operationId for transport-agnostic client implementations |
router method on the trait |
Wires all operations to an axum::Router when using openapi_trait::axum |
| Crate | Purpose |
|---|---|
openapi-trait |
Main entry point — add this to your Cargo.toml |
openapi-trait-axum |
Axum proc-macro — not for direct use |
openapi-trait-client |
Client proc-macro and ReqwestClient derive — not for direct use |
openapi-trait-shared |
Framework-agnostic codegen helpers — not for direct use |
Add to Cargo.toml:
[dependencies]
openapi-trait = "0.1"Then apply the macro to a mod block:
#[openapi_trait::axum("openapi/petstore.yaml")]
pub mod petstore {}Or generate a transport-agnostic client trait:
#[openapi_trait::client("openapi/petstore.yaml")]
pub mod petstore {}The path is resolved relative to the crate root (CARGO_MANIFEST_DIR). The
file is tracked by Cargo — the crate recompiles automatically when the spec
changes.
The generated trait name comes from the module name, so mod petstore {}
produces petstore::PetstoreApi and petstore::PetstoreClient.
openapi-trait enables the reqwest-client feature by default. That adds:
#[derive(openapi_trait::ReqwestClient)]for carrier structs that hold areqwest::Client- A blanket implementation of the generated
{ModName}Clienttrait for any type implementingopenapi_trait::ReqwestClientCore - Re-exports of
reqwestandpercent_encodingfor generated client code
#[openapi_trait::client("openapi/petstore.yaml")]
pub mod petstore {}
#[derive(Clone, openapi_trait::ReqwestClient)]
struct PetstoreClient {
#[openapi_trait(client)]
http: openapi_trait::reqwest::Client,
#[openapi_trait(base_url)]
endpoint: String,
}Disable default features if you only want the transport-agnostic trait:
[dependencies]
openapi-trait = { version = "0.1", default-features = false }| Feature | Status |
|---|---|
components/schemas → structs |
✅ |
| Path parameters | ✅ |
| Query parameters (including string enums) | ✅ |
| Header parameters | ✅ |
| Request bodies (JSON) | ✅ |
| Response enums per operation | ✅ |
allOf / anyOf / oneOf |
Partial — falls back to serde_json::Value |
| Security schemes | Not planned for v0.1 |