Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Option to infer response_model #620

Closed
dmontagu opened this issue Oct 13, 2019 · 8 comments
Closed

Option to infer response_model #620

dmontagu opened this issue Oct 13, 2019 · 8 comments
Labels
feature New feature or request

Comments

@dmontagu
Copy link
Collaborator

dmontagu commented Oct 13, 2019

Is your feature request related to a problem? Please describe.
Primarily for the sake of the generated OpenAPI spec (but also for security/validation reasons), I currently specify a response_model for nearly all of my endpoints, but for the vast majority of my endpoints, the response_model is the same as the type that the endpoint returns.

This means that I am frequently duplicating the response_model in the return annotation.

In order to reduce duplication/room for copying/refactoring mistakes, it would be great if it were possible to (optionally) infer the response_model from the endpoint signature.

It should still be possible to override the value, and it should be ignored if a raw Response class is returned, I would just want the default to be the annotated return type of the endpoint. Note: this implementation would also make it much easier to catch anywhere the response_model differed from the returned value's type while scanning through code.

To be clear, I don't think this needs to be the default behavior, I just would like the option to enable it.


I am currently using the following subclass of APIRouter in my code to enable this:

from typing import TYPE_CHECKING, Any, Callable, get_type_hints

from fastapi import APIRouter


class InferringRouter(APIRouter):
    if not TYPE_CHECKING:

        def add_api_route(self, path: str, endpoint: Callable[..., Any], **kwargs: Any) -> None:
            if kwargs.get("response_model") is None:
                kwargs["response_model"] = get_type_hints(endpoint).get("return")
            return super().add_api_route(path, endpoint, **kwargs)

    else:  # pragma: no cover
        pass

I think it would be nice if this capability was upstreamed though, both to ensure it is maintained, but also so others can benefit.


Describe the solution you'd like

Rather than using the above implementation (which has some problems -- for example, it isn't possible to specify None as the response_model with an annotated return type), I think it would be better if there were a boolean-valued keyword argument to the APIRouter initializer that, if enabled, would cause the default behavior to be to have the annotated return type used as the response_model by default.

@dmontagu dmontagu added the feature New feature or request label Oct 13, 2019
@tjerkw
Copy link

tjerkw commented Jan 11, 2020

Nice! I like this much better than duplicating it like this:

@app.get("/items/{item_id}", response_model=List[Order])
def get_orders() -> List[Order]:
    return []

@dmontagu
Copy link
Collaborator Author

For anyone following this thread, I've released fastapi-utils which includes the InferringRouter class above.

You can read the docs here: https://fastapi-utils.davidmontague.xyz/user-guide/inferring-router/

@erdnaxeli
Copy link

erdnaxeli commented Jan 21, 2020

I would love this!

To be honest when I was reading the doc it was not obvious why this was not already implemented. As all inputs use annotations, it feels like the output should too. Reading the fastapi-utils documentation actually gives me some advices on why someone would like it to not be the case. I think the official documentation could explain this choice like the fastapi-utils one does.

@tiangolo
Copy link
Member

Thanks for the clear explanation and argument @dmontagu . Yeah, it makes sense having it as an optional feature. And I see a lot of people would like that.

I think we might want to have a way to override the function annotation setting response_model=None explicitly. That would allow having the function annotated even for a specific case where we don't want the annotation to be used as a response model.

We could do something similar to what I did in Typer to handle all those defaults overridable with None: https://github.com/tiangolo/typer/blob/master/typer/models.py#L68 and here https://github.com/tiangolo/typer/blob/master/typer/main.py#L330 and then here: https://github.com/tiangolo/typer/blob/master/typer/main.py#L336

@tiangolo
Copy link
Member

On another note, about why that's not the default, it's in the Technical Details note here: https://fastapi.tiangolo.com/tutorial/response-model/

Could it be explained better? I see a lot of people ask about it although it's there, and I'm not sure how else should we document it to make it more understandable.

@erdnaxeli
Copy link

On another note, about why that's not the default, it's in the Technical Details note here: https://fastapi.tiangolo.com/tutorial/response-model/

Could it be explained better? I see a lot of people ask about it although it's there, and I'm not sure how else should we document it to make it more understandable.

I missed that, but it seems to be in the correct place and well explained.

@tjerkw
Copy link

tjerkw commented Feb 11, 2020 via email

@acnebs
Copy link

acnebs commented Apr 7, 2020

I think the API introduced by @dmontagu in fastapi_utils makes sense to integrate with FastAPI proper, with the addition of a response_model=None to override the return annotation as @tiangolo suggests.

Seems fairly trivial to handle this using a sentinel value.

@tiangolo tiangolo changed the title [FEATURE] Option to infer response_model Option to infer response_model Feb 24, 2023
@fastapi fastapi locked and limited conversation to collaborators Mar 4, 2023
@tiangolo tiangolo converted this issue into discussion #9209 Mar 4, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants