tamasfe / aide

An API documentation library
Apache License 2.0
417 stars 70 forks source link

Wrapping `Extension` with `Arc::new` fails at runtime - "Missing request extension: Extension of type `aide::openapi::openapi::OpenApi` was not found" #148

Closed elipp closed 1 month ago

elipp commented 1 month ago

The code (below) compiles and runs, but calling the /api.json endpoint results in the following runtime error:

Missing request extension: Extension of type aide::openapi::openapi::OpenApi was not found. Perhaps you forgot to add it? See axum::Extension

axum: 0.7.7 aide: 0.13.4

Using the code from documentation (the only modification being the Arc::new part, taken from examples/axum):

use std::sync::Arc;

// 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()
        .api_route("/hello", post(hello_user))
        .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(Arc::new(api)))
            .into_make_service(),
    )
    .await
    .unwrap();
}

If I remove the Arc, the program runs just fine.

0xdeafbeef commented 1 month ago

The code (below) compiles and runs, but calling the /api.json endpoint results in the following runtime error:

Missing request extension: Extension of type aide::openapi::openapi::OpenApi was not found. Perhaps you forgot to add it? See axum::Extension

axum: 0.7.7 aide: 0.13.4

Using the code from documentation (the only modification being the Arc::new part, taken from examples/axum):

use std::sync::Arc;

// 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()
        .api_route("/hello", post(hello_user))
        .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(Arc::new(api)))
            .into_make_service(),
    )
    .await
    .unwrap();
}

If I remove the Arc, the program runs just fine.

you are trying to extract OpenApi without Arc here

async fn serve_api(Extension(api): Extension<OpenApi>) -> impl IntoApiResponse {

But you're providing it wrapped in an Arc with:

   .layer(Extension(Arc::new(api)))

Of course it doesn't work because the types don't match.

Please read the docs

elipp commented 1 month ago

Sure, alright. "Of course" I should have realized that. Thanks