Search code examples
pythonfastapihttpresponseasgi

FastAPI Swagger Documentation Showing Incorrect Response Status?


The route implemented via FastAPI (code snippet below) is working fine.

@app.post(path='/user/create/')
async def create_user(response: Response,
                      email: str = Form(),
                      password: str = Form()) -> None:
    """
    Create a new user with this route.
    Possible responses:
    201: New User Created
    400: Bad email id supplied.
    409: User already signed up, hence causing conflict with existing id.
    """
    logging.info(msg=f'New user sign up request from {email}.')
    try:
        user: WebUser = WebUser(email=email, password=password)
        # Either 409 or 201
        response.status_code = [CONFLICT, CREATED][await user.create_new()]
    except ValidationError:
        logging.error(
            msg=f'User creation for {email} failed because of bad request.')
        response.status_code = BAD_REQUEST  # 400

But the FastAPI swagger documentation is showing incorrect (actually totally random) responses. Here is the screenshot.

enter image description here

So, it is not showing 409, and 400, but somehow showing 422 as a possible response. Also, the successful response is actually 201.

So how does FastAPI form these documentations behind the scene and am I doing something wrong to mislead it? Or is it a bug within FastAPI?


Solution

  • This is documented in the Additional Status Codes and Additional Responses in OpenAPI sections of the documentation.

    If we rewrite your code like this:

    import random
    import logging
    import pydantic
    from fastapi import FastAPI, Response, Form
    from fastapi.responses import JSONResponse
    
    app = FastAPI()
    
    
    class BaseModel(pydantic.BaseModel):
        pass
    
    
    class Message(BaseModel):
        message: str
    
    
    class WebUser(BaseModel):
        email: str
        password: str
    
    
    def user_create_new(user: WebUser):
        if random.randint(0, 100) > 50:
            pass
        else:
            raise ValueError()
    
    
    @app.post(
        path="/user/create",
        responses={400: {"model": Message}, 409: {"model": Message}},
    )
    async def create_user(
        response: Response, email: str = Form(), password: str = Form()
    ) -> None:
        logging.info(msg=f"New user sign up request from {email}.")
        user: WebUser = WebUser(email=email, password=password)
        try:
            user_create_new(user)
            return Message(message=f"Created user {email}")
        except ValueError:
            return JSONResponse(
                status_code=400, content=Message(message="Unable to create user").dict()
            )
    

    Then http://localhost/openapi gets us:

    enter image description here