axum
only.Expand description
Open API code generation for axum
.
The implementation closely mimics the api of axum
with
extra care taken in order to allow seamless transitions.
The notable types are ApiRouter
and ApiMethodRouter
that wrap
axum::Router
and axum::routing::MethodRouter
respectively.
Likewise, the top-level methods in axum::routing
have their counterparts
in routing
.
Examples
Take the following axum
example:
use axum::{response::IntoResponse, routing::post, Json, Router};
use serde::Deserialize;
#[derive(Deserialize)]
struct User {
name: String,
}
async fn hello_user(Json(user): Json<User>) -> impl IntoResponse {
format!("hello {}", user.name)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/hello", post(hello_user));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
We can apply the following changes to generate documentation for it:
// Replace some of the `axum::` types with `aide::axum::` ones.
use aide::{
axum::{
routing::{get, post},
ApiRouter, IntoApiResponse,
},
openapi::{Info, OpenApi},
};
use axum::{Extension, Json};
use schemars::JsonSchema;
use serde::Deserialize;
// We'll need to derive `JsonSchema` for
// all types that appear in the api documentation.
#[derive(Deserialize, JsonSchema)]
struct User {
name: String,
}
async fn hello_user(Json(user): Json<User>) -> impl IntoApiResponse {
format!("hello {}", user.name)
}
// Note that this clones the document on each request.
// To be more efficient, we could wrap it into an Arc,
// or even store it as a serialized string.
async fn serve_api(Extension(api): Extension<OpenApi>) -> impl IntoApiResponse {
Json(api)
}
#[tokio::main]
async fn main() {
let app = ApiRouter::new()
// Change `route` to `api_route` for the route
// we'd like to expose in the documentation.
.api_route("/hello", post(hello_user))
// We'll serve our generated document here.
.route("/api.json", get(serve_api));
let mut api = OpenApi {
info: Info {
description: Some("an example API".to_string()),
..Info::default()
},
..OpenApi::default()
};
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(
app
// Generate the documentation.
.finish_api(&mut api)
// Expose the documentation to the handlers.
.layer(Extension(api))
.into_make_service(),
)
.await
.unwrap();
}
Only routes added via api_route
are visible in the documentation,
this makes exposed routes explicit and less error-prone.
Adding details.
The above example includes routes and request parameters but it’s lacking response types and additional metadata such as descriptions, as these are not possible to infer just via types.
Responses
Generally we can add information at the following levels:
- Operation level (e.g.
get_with
) - Route level (
api_route_with
) - API-level (
finish_api_with
)
All of these are additive and the API-level information will not override route or operation metadata unless explicitly stated.
With this being said, we can specify the response status code
and the type for our hello_user
operation:
// ...
.api_route(
"/hello",
post_with(hello_user, |op| op.response::<200, String>()),
)
// ...
And on the API-level we define that in every unspecified case, we return some kind of text:
// ...
app.finish_api_with(&mut api, |api| api.default_response::<String>())
// ...
Other Metadata
We can extend our hello_user
operation with further metadata:
// ...
.api_route(
"/hello",
post_with(hello_user, |o| {
o.id("helloUser")
.description("says hello to the given user")
.response_with::<200, String, _>(|res| {
res.description("a simple message saying hello to the user")
.example(String::from("hello Tom"))
})
}),
)
// ...
Composability
Just like in axum
, nesting and merging routers is possible,
and the documented routes will be updated as expected.
Modules
Method routing that closely mimics axum::routing
while extending
it with API documentation-specific features..
Structs
A wrapper over axum::Router
that adds
API documentation-specific features.
Traits
A trait analogous to [IntoResponse
] that allows writing
impl IntoApiResponse
for documented handlers.
Axum’s IntoResponse
cannot be used for these handlers
since the return type has to implement OperationOutput
.
Convenience extension trait for axum::Router
.