utoipa icon indicating copy to clipboard operation
utoipa copied to clipboard

Specify schema by reference

Open thehabbos007 opened this issue 1 year ago • 2 comments

Hey!

It would be really nice to have an escape-hatch for situations where generating a JSON Schema for the type isn't trivial/possible. Is there a way to do that already, perhaps by inlining a JSON Schema?

What I have in mind, is a way to define a schema being "external" using the conventional relative references of OpenAPI + JSON Schema For example if I have a route (in Axum, but not relevant):

#[utoipa::path(
    post,
    path = "/entities",
    request_body = CreateEntityRequest,
    responses(
      (status = 201, content_type = "application/json", description = "Entity created successfully", body = "./Entity.json"),
    ),
)]
async fn create_entity(
    body: Json<CreateEntityTypeRequest>
) -> Result<Json<Entity>, impl IntoResponse> { .. }

I would like for the body attribute to be the value of the JSON Schema $ref, resulting in

"responses": {
  "201": {
    "description": "Entity created successfully",
    "content": {
      "application/json": {
        "schema": { "$ref": "./Entity.json" }
      }
    }
  }
}

where the OpenAPI Spec consumer would have to visit the relative ./Entity.json path, expecting it to be retrievable. Obviously this file could also be inlined in the spec at compile-time, but this seems like a reasonable middle-ground. Perhaps overloading body isn't ideal, but the general idea is that a path can be given in place of a concrete type.

Any thoughts? :)

thehabbos007 avatar Aug 01 '22 12:08 thehabbos007

Hi, unfortunately this is not supported at the moment. Although implementing support for this is somewhat simple. For example I could implement following syntax where ref(...) would translate above mentioned file reference syntax.

#[utoipa::path(
    post,
    path = "/entities",
    request_body = CreateEntityRequest,
    responses(
      (status = 201, description = "Entity created successfully", 
           body = ref("./Entity.json")),
    ),
)]
async fn create_entity(
    body: Json<CreateEntityTypeRequest>
) -> Result<Json<Entity>, impl IntoResponse> { .. }

But this needs bit of planning thus it would be beneficial to add the support not just to response bodies but to request bodies and others. I'll add it to the projects kanban board for later work.

Obviously this file could also be inlined in the spec at compile-time

This would be cool ability but need to see whether this can be achieved in feasible manner. But at least $ref object can be created if nothing.

Thanks for suggestion :)

juhaku avatar Aug 01 '22 14:08 juhaku

Cool, thanks for the insights and thoughts!

To share my workaround so far, I'm using an addon to replace any Ref component that starts with EXTERNAL_() with ./().json mutably. This works fine for my purposes :)

thehabbos007 avatar Aug 01 '22 14:08 thehabbos007

@thehabbos007 support for this is being added by the attached PR

juhaku avatar Dec 11 '22 20:12 juhaku