Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request - Community Feedback needed] Additional Responses #16

Closed
pulitz opened this issue Jan 30, 2019 · 20 comments
Closed

[Feature Request - Community Feedback needed] Additional Responses #16

pulitz opened this issue Jan 30, 2019 · 20 comments
Labels
answered community-feedback Community feedback required feature New feature or request reviewed

Comments

@pulitz
Copy link

pulitz commented Jan 30, 2019

How do we add additional responses (e.g. 401, 403, 500, etc..) to the documentation other than the default 200 response and the 422 validation error response?

@kurtrottmann
Copy link

Did you check this link https://fastapi.tiangolo.com/tutorial/response-status-code/ ?

@pulitz
Copy link
Author

pulitz commented Jan 31, 2019

@kurtrottmann I did, but that's only for changing the status code of the main (only) response. I'm wondering if it's possible to add documentations of the error responses.

@tiangolo
Copy link
Owner

Thanks @kurtrottmann for helping here! That's highly appreciated 🎉 🌮


@pulitz There's still no defined way to declare them.

I knew this would happen, but I didn't want to define the API/functionality for that yet, as I wanted to hear feedback for you guys (and other developers), before implementing it.


Here's the problem/situation:

When defining a single response model and status code, no matter what is actually returned by the path operation function, it will be converted to that model (and filtered by it) and that status code will be returned.

If we have more than one possible status code, the path operation function has to return a Starlette Response (instead of a plain dict or Pydantic model), with the final content already converted to the correct JSON data (maybe filtered by the correct Pydantic model) and the correct status code. That has to be done inside the path operation function, independently of the declared responses and status codes in the path operation decorator (the ones that appear in the OpenAPI generated schema).

This is because there is no way for FastAPI (actually for Python) to know what are all the possible values/data returned by the path operation function.

What has to be done:

So, this would mean, that the declared model and status code has to be declared twice (code repetition 😞 , that in this case, we can't avoid). Once in the path operation decorator (to declare it for the generated OpenAPI schema) and once inside the path operation function, to actually return it.

The question, requested feedback:

In summary, declaring additional response models/schemas and status codes will be "way more complex" than declaring a single one. Actually, it won't be that difficult, but will not be super-intuitive and simple, which is the ideal for FastAPI (and is what all the rest tries to achieve).

I can think of two options:

An additional parameter for the path operation decorator, something like extra_responses (is that parameter name intuitive enough? any alternatives?).

This parameter could receive:

  • Option A: A dict mapping status codes (numbers) to Pydantic models. The schema of those Pydantic models would be the optional response schemas for each status code.

    • This option would probably be the more simple and Pythonic, as Pydantic is just plain Python types. But it requires creating and using those additional classes.
    • By using additional Pydantic models, it would be possible to have them in the top level OpenAPI definitions and reference them, as is currently done for the response_model parameter. And they could be used by other OpenAPI based client code generators. For example, creating TypeScript interfaces automatically (if you don't know what that is, don't worry about the term).
  • Option B: A dict mapping status codes to sub-dicts with the actual JSON Schema (OpenAPI schema) of the response.

    • This option would require writing the actual output JSON Schema directly, or calling the schema method from Pydantic models by hand.
    • It would allow declaring additional responses inside of the same path operation decorator, without needing to declare anything else outside of the path operation. And would give the maximum flexibility, as the contents would be almost "free-form". Although, there's no gain in the "free-form" that I can think of, as Pydantic supports all (or almost all) the possible declarations with JSON Schema.
  • Option C: A dict mapping status codes to sub-dicts containing keys for the description (a str), headers (a dict), content (here's where the schemas live), the content could have the output schema as a dict directly, or we could add support for the example, examples, schema and encoding.

  • Option D: The same as Option C, but instead of having each schema as a dict, receive Pydantic models.

    • Options C and D are the most complex, also the most complete (with respect to OpenAPI).

References

OpenAPI operation object, that contains responses: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject

OpenAPI responses object that contains each response: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject

OpenAPI response object that contains each field, and content, referencing a "Media Type Object" (that contains the actual schema): https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject

OpenAPI Media Type object (that contains the actual schema): https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#mediaTypeObject


I know this gets hairy quickly, so I'll leave this issue open as a "feature request" to collect more feedback.

I know each of those options above is doable, but what I'm concerned the most is the style of the developer-facing API, I want it to be as friendly and intuitive as the rest of FastAPI, or at least as close as possible.

Actually, all the rest of FastAPI was designed similarly, first checking all the possible developer-facing functionality, in several editors, to ensure the best development experience for FastAPI users (developers), and then doing all the implementation to match that design.

What do you guys think?

@tiangolo tiangolo added feature New feature or request community-feedback Community feedback required labels Jan 31, 2019
@tiangolo tiangolo changed the title [Question] Additional Responses [Feature Request - Community Feedback needed] Additional Responses Jan 31, 2019
@pulitz
Copy link
Author

pulitz commented Jan 31, 2019

Currently, I am using FastAPI to create an interface to call AWS services through boto3. And AWS raise ClientErrors which most (if not all) have the same structure and documented.
For me I think I prefer having to add the decorator while passing a list of models or pre-defined dicts of the possible errors/responses.

@euri10
Copy link
Contributor

euri10 commented Feb 3, 2019

Would you have simple examples for A,B,C ? I'm not sure I see concretly what that means if used in a simple example.

So far for instance I got a post route that returns a 409 if user added is a duplicate, I wrote it this way and didn't feel the need for another black magic, but maybe that's because I was only concerned with the status_code

@users_router.post("/", status_code=HTTP_201_CREATED)
async def useradd(request: Request, userin: UserInRegister):
    query = users.insert().values(**userin.dict())
    try:
        await request.database.execute(query)
    except UniqueViolationError as e:
        return Response(content=None, status_code=409)
    return userin

@pulitz
Copy link
Author

pulitz commented Feb 3, 2019

@euri10 Functionality-wise, yes, I am also doing something similar. However, what I want are for those different responses to be reflected in the auto-generated documentation.

@euri10
Copy link
Contributor

euri10 commented Feb 3, 2019

@pulitz I understand now, totally missed the perspective before.

gonna read the openapi links now, what would be amazing to see in option A, B, C and D is a short example from a FastAPI user perspective

@wshayes
Copy link
Sponsor Contributor

wshayes commented Feb 4, 2019

I think Option A assuming I understand the options provided. I've no problem creating the response models. That's something I generally re-use anyway.

@haizaar
Copy link
Contributor

haizaar commented Feb 14, 2019

I'm for Option A but with a small change - instead of mapping response codes directly to pydantic model, I'd map it to sub-dict that contains additional metadata useful for enriching generated OpenAPI. Similar to Path Operation Configuration. E.g.:

{
   409: {
        "model": Item,
        "description": "The item was updated on the server. Refresh your copy and try again",
        ...
    }
}

This way we can still keep it Pythonic while covering majority of use-cases (every abstraction has trade-offs and as someone who wrote thousands over thousands lines of OpenAPI yamls in the last years I'm very happy that I don't need to it anymore with FastAPI). So far FastAPI keeps OpenAPI behind the scenes (great!) and I believe it should continue this way.

@troyswanson
Copy link

troyswanson commented Feb 18, 2019

☝️This seems like option D to me.

Maybe a hybrid where if the property of the status code is a:

  • Pydantic class, it uses option A
  • dict, it uses option D

Could get the best of both worlds?

@haizaar
Copy link
Contributor

haizaar commented Feb 19, 2019

@troyswanson IMHO hybrid approach is too magical. But I understand it's subjective.

@hiovidiu
Copy link

Just chiming in to say I would really like to have this feature. Documenting multiple responses and errors is important for me.

@ricardomomm
Copy link
Contributor

+1

@loudsquelch
Copy link

This would be a great feature to have. Agree with @haizaar.

@tiangolo
Copy link
Owner

Thanks for the feedback guys. This is up there in the priority list, along with some things that are missing (like docs about testing).

@tiangolo
Copy link
Owner

tiangolo commented Apr 5, 2019

Guys, we have it 🎉 🚀

It's in between the hybrid and explicit approach, helping minimize code as possible but keeping all the flexibility.

Here are the new docs: https://fastapi.tiangolo.com/tutorial/additional-responses/

And the extended docs for bigger applications: https://fastapi.tiangolo.com/tutorial/bigger-applications/#add-some-custom-tags-and-responses

It's avaliable in version 0.12.0


There's a new parameter responses that receives a dict. It contains as keys the status codes (as int or str) and the value of each of those status codes is another dict for each additional response.

Each of these additional response dicts can have a key model with a Pydantic model. If it is found, its JSON Schema is generated and included in the right place.

This additional response dict can contain, apart from the optional model, everything available/valid in OpenAPI responses. Including description, headers, links, and content. The key content has a dict with media types as keys (e.g. application/json), and the values are another sub-dict, it can have a schema, it can also have example, examples, etc.

If one of these response dicts contains content, with a media type application/json, and more metadata inside, and he same resonse dict also contains a key model with a Pydantic model, all the original response is used and the JSON Schema from the Pydantic model is added to the schema key, but keeping all the additional metadata that was added.

It means that, you can use the parts that you need, and/or combine them.

For example, you can define a Pydantic model for a response, but declare that the same response can also return an image (or any other content type) apart from the JSON from the Pydantic model.

@mostaphaRoudsari
Copy link
Contributor

@tiangolo the additional responses are added but they are added to to all the endpoints that are using the same APIRouter.

PS: I just saw that someone else has already reported it here #139 with more details.

@tiangolo
Copy link
Owner

tiangolo commented Apr 5, 2019

Thanks @mostaphaRoudsari ! Yep, it should be fixed in #140 , released in in version 0.12.1. 🎉

@tiangolo
Copy link
Owner

As this was solved a while ago, I'll close this issue now. But feel free to add more comments or create new issues.

@jordyortiz2011
Copy link

jordyortiz2011 commented May 14, 2024

I Would like to add the header in a response, using a model Pydantic, but the documentation mentioned is not availibled, how can i do it?
The link is not working: https://fastapi.tiangolo.com/tutorial/additional-responses/

Captura de pantalla 2024-05-14 181214

I would like to add a Header, like this:

imagen

Somebody knows, how i can do this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
answered community-feedback Community feedback required feature New feature or request reviewed
Projects
None yet
Development

No branches or pull requests