16

I am writing an app where I need to have two completely different set of response structures depending on logic.

Is there any way to handle this so that I can have two different response models serialized, validated and returned and reflect in OpenAPI JSON?

I am using pydantic to write models.

Paolo
  • 20,112
  • 21
  • 72
  • 113
Dhaval Savalia
  • 495
  • 5
  • 17

2 Answers2

12

Yes this is possible. You can use Union for that in the response_model= parameter in your path decorator (I used the new python 3.10 style below). Here is a full example, this will work as is.

from typing import Union

from fastapi import FastAPI, Query
from pydantic import BaseModel

class responseA(BaseModel):
    name: str

class responseB(BaseModel):
    id: int

app = FastAPI()

@app.get("/", response_model=Union[responseA,responseB])
def base(q: int|str = Query(None)):
    if q and isinstance(q, str):
        return responseA(name=q)
    if q and isinstance(q, int):
        return responseB(id=q)
    raise HTTPException(status_code=400, detail="No q param provided")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000,  )

EDIT: As pointed out by TomášLinhart in the comments below, the response_model parameter must use Union. In the original answer, I used the 3.10 style responseA|responseB which did not work for everybody as explained here in the docs.
Result in your documentation: enter image description here

JarroVGIT
  • 4,291
  • 1
  • 17
  • 29
  • 4
    An alternative for previous python versions could be `@app.get("/", response_model=Union[responseA, responseB])` – Duarte P Jul 09 '22 at 11:39
  • Is it normal that pyright reports that : `Argument of type "Type[FirstType] | Type[SecondType]" cannot be assigned to parameter "response_model" of type "type | None" in function "get"` ? Using both the Union and the pipe syntaxe – adxl Feb 09 '23 at 09:47
  • This example did not work for me, I only see the `responseA` on the swagger UI. For reference I am using python 3.10 with fastapi 0.92.0 – Rohit Mar 23 '23 at 15:20
  • This doesn't seem to be correct. The [documentation](https://fastapi.tiangolo.com/tutorial/extra-models/#union-in-python-310) explicitely states that we have to use `Union[responseA, responseB]` instead of `responseA | responseB`. – Tomáš Linhart Mar 30 '23 at 18:59
  • @TomášLinhart; one is the equivalent of the other. The `|` notation was introduced in python 3.10. It is the exact equivalent of `Union`. – JarroVGIT Mar 31 '23 at 20:17
  • 1
    @JarroVGIT did you read the documentation I linked? There's explicitly stated that it's not the same thing in the given context. – Tomáš Linhart Apr 01 '23 at 05:35
  • `response_model = response B | response A` don't work. Can someone explain why the order matters? – shawnngtq Apr 02 '23 at 02:59
  • 1
    @TomášLinhart well I stand corrected! One learns everyday, I will amend the answer. Weird thing is: I tested this code before I posted it and then it worked. I thought maybe this is something new, but the docs on that part are 2 years old (so not new). Never knew though, thanks for pointing this out! – JarroVGIT Apr 02 '23 at 06:51
  • the correct multiple example should be with dropdown – Dr.X May 25 '23 at 13:02
1

If you need response multiple examples, you can try this:

@router.get(
    '/{UserId}',
    summary='get user',
    responses={
        200: {
            "description": "",
            "content": {
                "application/json": {
                    "examples": {
                        "Corporate user": {
                            'value': {
                                'foo': 'bar',
                            },
                        },
                        "Standard user": {
                            'value': {
                                'doo': 'www',
                            },
                        },
                    }
                }
            }
        }
    }
)

enter image description here

Dr.X
  • 853
  • 9
  • 15