Module aide::axum

source ·
Available on crate feature 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));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();

    axum::serve(listener, app).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()
    };

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();

    axum::serve(
        listener,
        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:

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§

Traits§