Fastapi: Typehints instead of response_model

Created on 4 Nov 2020  路  10Comments  路  Source: tiangolo/fastapi

First check

  • [x] I added a very descriptive title to this issue.
  • [x] I used the GitHub search to find a similar issue and didn't find it.
  • [x] I searched the FastAPI documentation, with the integrated search.
  • [x] I already searched in Google "How to X in FastAPI" and didn't find any information.
  • [x] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [x] I already checked if it is not related to FastAPI but to Pydantic.
  • [x] I already checked if it is not related to FastAPI but to Swagger UI.
  • [x] I already checked if it is not related to FastAPI but to ReDoc.
  • [x] After submitting this, I commit to:

    • Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

    • Or, I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

    • Implement a Pull Request for a confirmed bug.

Example

Now:

from fastapi import FastAPI
from typing import Dict, List
app = FastAPI()


@app.get("/", response_model=Dict[str, List[str]])
def read_root():
    return {"Hello": ["tom", "hesus"]}

The solution you would like

I want to do it through typehints:

from fastapi import FastAPI
from typing import Dict, List
app = FastAPI()


@app.get("/")
def read_root() -> Dict[str, List[str]]:
    return {"Hello": ["tom", "hesus"]}
enhancement

Most helpful comment

@peach-lasagna There were multiple discussions and implementation of this feature.

PR #1436, #875, and issue #101 they all about this.

Basically, I was an author of #1436 and after reading discussion thread #101, I understood the main idea why this feature isn't implemented.

In my opinion, the best solution to this problem will be to implement this feature but not to break the existing code base.
One of the possible solutions is:

  1. In case if response_model is set then use it.
  2. In case if response_model isn't set use return type annotation as response_model.
# response model - Dict[str, List[str]]
@app.get("/", response_model=Dict[str, List[str]])
def read_root():
    return {"Hello": ["tom", "hesus"]}

# response model - Dict[str, List[str]]
@app.get("/")
def read_root() -> Dict[str, List[str]]:
    return {"Hello": ["tom", "hesus"]}

# response model - Dict[str, List[str]]
@app.get("/", response_model=Dict[str, List[str]])
def read_root() -> Dict[str, Set[str]]:
    return {"Hello": {"tom", "hesus"}}

In case if we accept the proposal above we can simply reopen and merge PR #1436 because it has already implemented the logic described above.

I really like this idea, and a lot of peoples want this feature.

All 10 comments

I don't know if it was only a decision or if there's an issue on the suggested approach.

My input here will be related to MyPy:

from typing import Dict, List

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Potato(BaseModel):
    name: str
    color: str

@app.get("/")
def get_potato() -> Potato:
    return {"name": "best potato", "color": "purple"}

Doing as above, MyPy will get sad:

test.py:14: error: Incompatible return value type (got "Dict[str, str]", expected "Potato")
Found 1 error in 1 file (checked 1 source file)

Maybe a "FastAPI MyPy plugin" can be created to allow it (in case the suggestion is accepted and if is possible to create a plugin like that, I've never tried).

No, i want use annotations instead of response_model. i want fastapi to understand typehints

I understand what you said... I want that too. :eyes:

i think to do something like this:

from typing import get_type_hints

@app.get("/")
async def root() -> dict:
    return {"hello": "world"}

hints = get_type_hints(root)
if "return" in hints:
    response_model  = hints["return"]

# or
# response_model = hints.get("return")

There is already a library that allows you to do this:

https://fastapi-utils.davidmontague.xyz/user-guide/inferring-router/

thx! @tiangolo , how about do it in APIRoter by default?

That would break existing usage patterns, as right now you can, for example, return a dictionary, and have fastapi validate and transform it into a pydantic model. If the pattern was changed this would no longer be possible without breaking type validation

@peach-lasagna There were multiple discussions and implementation of this feature.

PR #1436, #875, and issue #101 they all about this.

Basically, I was an author of #1436 and after reading discussion thread #101, I understood the main idea why this feature isn't implemented.

In my opinion, the best solution to this problem will be to implement this feature but not to break the existing code base.
One of the possible solutions is:

  1. In case if response_model is set then use it.
  2. In case if response_model isn't set use return type annotation as response_model.
# response model - Dict[str, List[str]]
@app.get("/", response_model=Dict[str, List[str]])
def read_root():
    return {"Hello": ["tom", "hesus"]}

# response model - Dict[str, List[str]]
@app.get("/")
def read_root() -> Dict[str, List[str]]:
    return {"Hello": ["tom", "hesus"]}

# response model - Dict[str, List[str]]
@app.get("/", response_model=Dict[str, List[str]])
def read_root() -> Dict[str, Set[str]]:
    return {"Hello": {"tom", "hesus"}}

In case if we accept the proposal above we can simply reopen and merge PR #1436 because it has already implemented the logic described above.

I really like this idea, and a lot of peoples want this feature.

This would make FastAPI's usage of type annotations much more natural, especially for new users familiar with how type annotations work outside of FastAPI. As a new user, it was very surprising to need to put the return type into response_model.

@johnthagen Totally agree with you, I faced the same problem when have started working with FastAPI.

@tiangolo What do you think about this feature? We can implement this feature fully backward compatible.

Was this page helpful?
0 / 5 - 0 ratings