Fastapi: [QUESTION] Documenting multiple possible error models for a single response code

Created on 11 Sep 2019  Â·  12Comments  Â·  Source: tiangolo/fastapi

Description

How can I list multiple possible error responses so they're documented. I've tried supplying multiple error models in the model field of the responses dict with no success:

authorize_params = {"path" : "/authorize",
                    "summary" : "authorize",
                    "responses" : {200 : {"description" : "OK"},
                                   302 : {"description" : "Redirecting to requested URL."},
                                   400 : {"description" : "Bad Request",
                                          "model" : [InvalidOAuthClientIDError, DisabledOAuthClientIDError]},
                                   401 : {"description" : "Unauthorized"}, 
                                   403 : {"description" : "Forbidden"}}}
question

All 12 comments

you have an example in the docs here:
https://fastapi.tiangolo.com/tutorial/additional-responses/#combining-information

On Wed, Sep 11, 2019 at 12:22 AM Jason J. W. Williams <
[email protected]> wrote:

Description

How can I list multiple possible error responses so they're documented.
I've tried supplying multiple error models in the model field of the
responses dict with no success:

authorize_params = {"path" : "/authorize",
"summary" : "authorize",
"responses" : {200 : {"description" : "OK"},
302 : {"description" : "Redirecting to requested URL."},
400 : {"description" : "Bad Request",
"model" : [InvalidOAuthClientIDError, DisabledOAuthClientIDError]},
401 : {"description" : "Unauthorized"},
403 : {"description" : "Forbidden"}}}

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/tiangolo/fastapi/issues/518?email_source=notifications&email_token=AAINSPSYPVAW4P67UVK5EV3QJAMY5A5CNFSM4IVNFZDKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HKSGUPA,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAINSPTLHF2VZJMUCNNKZZ3QJAMY5ANCNFSM4IVNFZDA
.

--
benoit barthelet
http://pgp.mit.edu/pks/lookup?op=get&search=0xF150E01A72F6D2EE

I don't see an example there combining multiple error models for a single response code.

ok that wasn't clear to me at 1st sight that it was multiple response for the same status code, I'd change the title.
it should indeed be possible afaik using oneOf, if you take the following for instance:

 '400':
          description: msg
          content:
            application/json:
              schema:
                 oneOf:
                  - $ref: '#/components/schemas/MessageOne'
                  - $ref: '#/components/schemas/MessageTwo'

how to produce it in FastAPI I don't know, that's a good question for the pydantic wizard @dmontagu

anyway this wont be displayed in the ui (https://github.com/swagger-api/swagger-ui/issues/3803)

Will redoc pick it up?

On Sep 12, 2019, at 01:02, euri10 notifications@github.com wrote:

ok that wasn't clear to me at 1st sight that it was multiple response for the same status code, I'd change the title.
it should indeed be possible afaik using oneOf, if you take the following for instance:

'400':
description: msg
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/MessageOne'
- $ref: '#/components/schemas/MessageTwo'
how to produce it in FastAPI I don't know, that's a good question for the pydantic wizard @dmontagu

anyway this wont be displayed in the ui (swagger-api/swagger-ui#3803)

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or mute the thread.

I would also like to know if this is possible. 422 is quite a broad error code and i would like to have oneOf:

{ "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string" } ] }

or

{ "detail": [ { "msg": "string", "type": "string" } ] }

Okay, so for starters, you can always manually specify the dict you want to put there, so even in the worst case, you can just write the schema you want by hand.


However, there is a way to wrangle pydantic to do what you are trying to do, using a custom __root__ which is a Union:

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    id: str
    value: str


class Message(BaseModel):
    message: str


class Other(BaseModel):
    other: str


class MessageOrOther(BaseModel):
    __root__: Union[Message, Other]


app = FastAPI()


@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": MessageOrOther}})
async def read_item(item_id: str):
    pass


print(app.openapi())

...

        "responses": {
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageOrOther"

...

      "MessageOrOther": {
        "title": "MessageOrOther",
        "anyOf": [
          {
            "$ref": "#/components/schemas/Message"
          },
          {
            "$ref": "#/components/schemas/Other"
          }
        ]
      },

...

That said, for what it's worth, I would generally discourage the use of different schemas for the same status code.

The status code provides a natural discriminator to use to determine which model to use to deserialize a response, and so having one schema for a single response means it will be a lot easier to create a generated client that can deserialize your other possible error responses.

And even if you don't generate the client, in many languages it is still much easier to deserialize data if you don't need to introspect it in order to figure out what type of object it should be deserialized into. Obviously this is less of an issue with most dynamically typed languages, but if you might want to have a mobile client, or any more strongly typed SDK, it's something to consider.

Ultimately every situation is different, and there are lots of reasons why multiple documented responses might be right for any one application, but if you are on the fence it might be worth reconsidering using multiple possible response shapes for a single error code.

thanks for the detailed response @dmontagu

I ended up coming up with a similar solution to what you posted in the end which seems to work well. As an aside, there seems to be a bug in openapi anyway which prevents examples of both models displaying. see: https://github.com/swagger-api/swagger-ui/issues/3803

Yeah, that doesn't surprise me, there a lot of ways that swagger will break if you try to do things off the beaten path.

It’s not that the different errors have different structural schemas. In our case they all have error_code, error_class and error_description fields. We want to automate the documentation for the different error codes and classes that fill those fields when subclass. Right now documenting that is a manual process and that makes it easy to add an error code and forget to document it.

On Jan 21, 2020, at 03:58, David Montague notifications@github.com wrote:


Okay, so for starters, you can always manually specify the dict you want to put there, so even in the worst case, you can just write the schema you want by hand.

However, there is a way to wrangle pydantic to do what you are trying to do, using a custom __root__ which is a Union:

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
id: str
value: str

class Message(BaseModel):
message: str

class Other(BaseModel):
other: str

class MessageOrOther(BaseModel):
__root__: Union[Message, Other]

app = FastAPI()

@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": MessageOrOther}})
async def read_item(item_id: str):
pass

print(app.openapi())

...

    "responses": {
      "404": {
        "description": "Not Found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/MessageOrOther"

...

  "MessageOrOther": {
    "title": "MessageOrOther",
    "anyOf": [
      {
        "$ref": "#/components/schemas/Message"
      },
      {
        "$ref": "#/components/schemas/Other"
      }
    ]
  },

...
That said, for what it's worth, I would generally discourage the use of different schemas for the same status code.

The status code provides a natural discriminator to use determine which model to use to deserialize a response, and so having one schema for a single response means it will be a lot easier to create a generated client that can deserialize your other possible error responses.

And even if you don't generate the client, in many languages it is still much easier to deserialize data if you don't need to introspect it in order to figure out what type of object it should be deserialized into. Obviously this is less of an issue with most dynamically typed languages, but if you might want to have a mobile client, or any more strongly typed SDK, it's something to consider.

Ultimately every situation is different, and there are lots of reasons why multiple documented responses might be right for any one application, but if you are on the fence it might be worth reconsidering using multiple possible response shapes for a single error code.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.

Yep, just as @dmontagu said above, that would probably be the best way to go.

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

I wanted to ask this question:

To satisfy the following usecase:

      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BoundaryRetrieve'
        default:
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

How shall we define the APIs?

With the below API declaration, we can have proper status codes and models defined for (un)successful calls. But how to add the default model which would be reflected in swagger.json in case of responses with other status codes from what we have defined?.

We do have the error handling addressed in the code base, but for sake of the client gen, we want this value to come automatically with the help of maybe some attributes, etc. instead of adding it manually.

@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": MessageOrOther}})
async def read_item(item_id: str):

In Csharp we can use [ProducesDefaultResponseType(typeof(ErrorResponse))], to have it.
Do we need to create a custom filter to support this usecase?

@tiangolo, @dmontagu

Was this page helpful?
0 / 5 - 0 ratings