Turn an OpenAPI spec into Rust traits at compile time. Implement the trait
to get a typed axum server, or use the
generated trait as a transport-agnostic client with a ready-made reqwest
implementation. You don't check in generated files or write a build.rs, and
the code always matches the spec.
📖 Read the guide · 📚 API reference
[dependencies]
openapi-trait = "1"#[openapi_trait::axum("openapi/pets.yaml")]
pub mod pets {}
#[derive(Clone)]
struct PetServer;
impl pets::PetsApi for PetServer {
type Error = pets::NotImplemented;
async fn get_pet(
&self,
ctx: pets::get_pet::Context,
) -> Result<pets::get_pet::Response, Self::Error> {
Ok(pets::get_pet::Response::Status200(pets::Pet {
id: ctx.req.pet_id,
name: "doggie".into(),
status: None,
}))
}
// Operations you don't implement yet answer `500 not implemented`.
}
use pets::PetsApi as _;
let app: axum::Router = PetServer.router();#[openapi_trait::client("openapi/pets.yaml")]
pub mod pets {}
#[derive(Clone, openapi_trait::ReqwestClient)]
struct PetsHttpClient {
#[openapi_trait(client)]
http: openapi_trait::reqwest::Client,
#[openapi_trait(base_url)]
base_url: String,
#[openapi_trait(auth)]
auth: pets::PetsAuthState,
}
use pets::{PetsClient as _, PetsClientAuth as _};
let client = PetsHttpClient {
http: openapi_trait::reqwest::Client::new(),
base_url: "http://localhost:3000".into(),
auth: Default::default(),
}
.with_bearer_auth("secret");
let pet = client.get_pet(pets::get_pet::Request { pet_id: 1 }, None).await?;pets::PetsClient is a plain trait, so you can also implement it yourself as a
test double or on top of another transport.
Runnable versions of both examples are in
openapi-trait/examples.
- Models. Structs and enums from
components/schemas, includingoneOf/anyOf/allOf,additionalProperties, anddate-time/uuidformats. - Typed operations. A module per operation with a
Requeststruct for path, query, header and body inputs, and aResponseenum with one variant per status code. - Security.
apiKey, HTTP bearer and HTTP basic. On the server, credentials are extracted and missing ones get a401. On the client, they are injected into every call. - Streaming. Binary request and response bodies are streamed, never buffered.
- Validation (opt-in). Schema constraints become
serde_validrules. - Go-to-definition. Add
source_file(#[openapi_trait::axum("openapi/pets.yaml", source_file)]) and F12 in your IDE opens the generated code. It is written to a self-gitignored.openapi-trait/folder (details). - Debug dump. Set
OPENAPI_TRAIT_DEBUG=1to write the generated code to disk.
See the OpenAPI support matrix for details.
MIT