Movatterモバイル変換


[0]ホーム

URL:


Skip to content
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

Response Model - Return Type

You can declare the type used for the response by annotating thepath operation functionreturn type.

You can usetype annotations the same way you would for input data in functionparameters, you can use Pydantic models, lists, dictionaries, scalar values like integers, booleans, etc.

fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=Nonetags:list[str]=[]@app.post("/items/")asyncdefcreate_item(item:Item)->Item:returnitem@app.get("/items/")asyncdefread_items()->list[Item]:return[Item(name="Portal Gun",price=42.0),Item(name="Plumbus",price=32.0),]

FastAPI will use this return type to:

  • Validate the returned data.
    • If the data is invalid (e.g. you are missing a field), it means thatyour app code is broken, not returning what it should, and it will return a server error instead of returning incorrect data. This way you and your clients can be certain that they will receive the data and the data shape expected.
  • Add aJSON Schema for the response, in the OpenAPIpath operation.
    • This will be used by theautomatic docs.
    • It will also be used by automatic client code generation tools.

But most importantly:

  • It willlimit and filter the output data to what is defined in the return type.
    • This is particularly important forsecurity, we'll see more of that below.

response_model Parameter

There are some cases where you need or want to return some data that is not exactly what the type declares.

For example, you could want toreturn a dictionary or a database object, butdeclare it as a Pydantic model. This way the Pydantic model would do all the data documentation, validation, etc. for the object that you returned (e.g. a dictionary or database object).

If you added the return type annotation, tools and editors would complain with a (correct) error telling you that your function is returning a type (e.g. a dict) that is different from what you declared (e.g. a Pydantic model).

In those cases, you can use thepath operation decorator parameterresponse_model instead of the return type.

You can use theresponse_model parameter in any of thepath operations:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • etc.
fromtypingimportAnyfromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float|None=Nonetags:list[str]=[]@app.post("/items/",response_model=Item)asyncdefcreate_item(item:Item)->Any:returnitem@app.get("/items/",response_model=list[Item])asyncdefread_items()->Any:return[{"name":"Portal Gun","price":42.0},{"name":"Plumbus","price":32.0},]

Note

Notice thatresponse_model is a parameter of the "decorator" method (get,post, etc). Not of yourpath operation function, like all the parameters and body.

response_model receives the same type you would declare for a Pydantic model field, so, it can be a Pydantic model, but it can also be, e.g. alist of Pydantic models, likeList[Item].

FastAPI will use thisresponse_model to do all the data documentation, validation, etc. and also toconvert and filter the output data to its type declaration.

Tip

If you have strict type checks in your editor, mypy, etc, you can declare the function return type asAny.

That way you tell the editor that you are intentionally returning anything. But FastAPI will still do the data documentation, validation, filtering, etc. with theresponse_model.

response_model Priority

If you declare both a return type and aresponse_model, theresponse_model will take priority and be used by FastAPI.

This way you can add correct type annotations to your functions even when you are returning a type different than the response model, to be used by the editor and tools like mypy. And still you can have FastAPI do the data validation, documentation, etc. using theresponse_model.

You can also useresponse_model=None to disable creating a response model for thatpath operation, you might need to do it if you are adding type annotations for things that are not valid Pydantic fields, you will see an example of that in one of the sections below.

Return the same input data

Here we are declaring aUserIn model, it will contain a plaintext password:

fromfastapiimportFastAPIfrompydanticimportBaseModel,EmailStrapp=FastAPI()classUserIn(BaseModel):username:strpassword:stremail:EmailStrfull_name:str|None=None# Don't do this in production!@app.post("/user/")asyncdefcreate_user(user:UserIn)->UserIn:returnuser

Info

To useEmailStr, first installemail-validator.

Make sure you create avirtual environment, activate it, and then install it, for example:

$pipinstallemail-validator

or with:

$pipinstall"pydantic[email]"

And we are using this model to declare our input and the same model to declare our output:

fromfastapiimportFastAPIfrompydanticimportBaseModel,EmailStrapp=FastAPI()classUserIn(BaseModel):username:strpassword:stremail:EmailStrfull_name:str|None=None# Don't do this in production!@app.post("/user/")asyncdefcreate_user(user:UserIn)->UserIn:returnuser

Now, whenever a browser is creating a user with a password, the API will return the same password in the response.

In this case, it might not be a problem, because it's the same user sending the password.

But if we use the same model for anotherpath operation, we could be sending our user's passwords to every client.

Danger

Never store the plain password of a user or send it in a response like this, unless you know all the caveats and you know what you are doing.

Add an output model

We can instead create an input model with the plaintext password and an output model without it:

fromtypingimportAnyfromfastapiimportFastAPIfrompydanticimportBaseModel,EmailStrapp=FastAPI()classUserIn(BaseModel):username:strpassword:stremail:EmailStrfull_name:str|None=NoneclassUserOut(BaseModel):username:stremail:EmailStrfull_name:str|None=None@app.post("/user/",response_model=UserOut)asyncdefcreate_user(user:UserIn)->Any:returnuser

Here, even though ourpath operation function is returning the same input user that contains the password:

fromtypingimportAnyfromfastapiimportFastAPIfrompydanticimportBaseModel,EmailStrapp=FastAPI()classUserIn(BaseModel):username:strpassword:stremail:EmailStrfull_name:str|None=NoneclassUserOut(BaseModel):username:stremail:EmailStrfull_name:str|None=None@app.post("/user/",response_model=UserOut)asyncdefcreate_user(user:UserIn)->Any:returnuser

...we declared theresponse_model to be our modelUserOut, that doesn't include the password:

fromtypingimportAnyfromfastapiimportFastAPIfrompydanticimportBaseModel,EmailStrapp=FastAPI()classUserIn(BaseModel):username:strpassword:stremail:EmailStrfull_name:str|None=NoneclassUserOut(BaseModel):username:stremail:EmailStrfull_name:str|None=None@app.post("/user/",response_model=UserOut)asyncdefcreate_user(user:UserIn)->Any:returnuser

So,FastAPI will take care of filtering out all the data that is not declared in the output model (using Pydantic).

response_model or Return Type

In this case, because the two models are different, if we annotated the function return type asUserOut, the editor and tools would complain that we are returning an invalid type, as those are different classes.

That's why in this example we have to declare it in theresponse_model parameter.

...but continue reading below to see how to overcome that.

Return Type and Data Filtering

Let's continue from the previous example. We wanted toannotate the function with one type, but we wanted to be able to return from the function something that actually includesmore data.

We want FastAPI to keepfiltering the data using the response model. So that even though the function returns more data, the response will only include the fields declared in the response model.

In the previous example, because the classes were different, we had to use theresponse_model parameter. But that also means that we don't get the support from the editor and tools checking the function return type.

But in most of the cases where we need to do something like this, we want the model just tofilter/remove some of the data as in this example.

And in those cases, we can use classes and inheritance to take advantage of functiontype annotations to get better support in the editor and tools, and still get the FastAPIdata filtering.

fromfastapiimportFastAPIfrompydanticimportBaseModel,EmailStrapp=FastAPI()classBaseUser(BaseModel):username:stremail:EmailStrfull_name:str|None=NoneclassUserIn(BaseUser):password:str@app.post("/user/")asyncdefcreate_user(user:UserIn)->BaseUser:returnuser

With this, we get tooling support, from editors and mypy as this code is correct in terms of types, but we also get the data filtering from FastAPI.

How does this work? Let's check that out. 🤓

Type Annotations and Tooling

First let's see how editors, mypy and other tools would see this.

BaseUser has the base fields. ThenUserIn inherits fromBaseUser and adds thepassword field, so, it will include all the fields from both models.

We annotate the function return type asBaseUser, but we are actually returning aUserIn instance.

The editor, mypy, and other tools won't complain about this because, in typing terms,UserIn is a subclass ofBaseUser, which means it's avalid type when what is expected is anything that is aBaseUser.

FastAPI Data Filtering

Now, for FastAPI, it will see the return type and make sure that what you return includesonly the fields that are declared in the type.

FastAPI does several things internally with Pydantic to make sure that those same rules of class inheritance are not used for the returned data filtering, otherwise you could end up returning much more data than what you expected.

This way, you can get the best of both worlds: type annotations withtooling support anddata filtering.

See it in the docs

When you see the automatic docs, you can check that the input model and output model will both have their own JSON Schema:

And both models will be used for the interactive API documentation:

Other Return Type Annotations

There might be cases where you return something that is not a valid Pydantic field and you annotate it in the function, only to get the support provided by tooling (the editor, mypy, etc).

Return a Response Directly

The most common case would bereturning a Response directly as explained later in the advanced docs.

fromfastapiimportFastAPI,Responsefromfastapi.responsesimportJSONResponse,RedirectResponseapp=FastAPI()@app.get("/portal")asyncdefget_portal(teleport:bool=False)->Response:ifteleport:returnRedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")returnJSONResponse(content={"message":"Here's your interdimensional portal."})

This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass of)Response.

And tools will also be happy because bothRedirectResponse andJSONResponse are subclasses ofResponse, so the type annotation is correct.

Annotate a Response Subclass

You can also use a subclass ofResponse in the type annotation:

fromfastapiimportFastAPIfromfastapi.responsesimportRedirectResponseapp=FastAPI()@app.get("/teleport")asyncdefget_teleport()->RedirectResponse:returnRedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")

This will also work becauseRedirectResponse is a subclass ofResponse, and FastAPI will automatically handle this simple case.

Invalid Return Type Annotations

But when you return some other arbitrary object that is not a valid Pydantic type (e.g. a database object) and you annotate it like that in the function, FastAPI will try to create a Pydantic response model from that type annotation, and will fail.

The same would happen if you had something like aunion between different types where one or more of them are not valid Pydantic types, for example this would fail 💥:

fromfastapiimportFastAPI,Responsefromfastapi.responsesimportRedirectResponseapp=FastAPI()@app.get("/portal")asyncdefget_portal(teleport:bool=False)->Response|dict:ifteleport:returnRedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")return{"message":"Here's your interdimensional portal."}

...this fails because the type annotation is not a Pydantic type and is not just a singleResponse class or subclass, it's a union (any of the two) between aResponse and adict.

Disable Response Model

Continuing from the example above, you might not want to have the default data validation, documentation, filtering, etc. that is performed by FastAPI.

But you might want to still keep the return type annotation in the function to get the support from tools like editors and type checkers (e.g. mypy).

In this case, you can disable the response model generation by settingresponse_model=None:

fromfastapiimportFastAPI,Responsefromfastapi.responsesimportRedirectResponseapp=FastAPI()@app.get("/portal",response_model=None)asyncdefget_portal(teleport:bool=False)->Response|dict:ifteleport:returnRedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")return{"message":"Here's your interdimensional portal."}

This will make FastAPI skip the response model generation and that way you can have any return type annotations you need without it affecting your FastAPI application. 🤓

Response Model encoding parameters

Your response model could have default values, like:

fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float=10.5tags:list[str]=[]items={"foo":{"name":"Foo","price":50.2},"bar":{"name":"Bar","description":"The bartenders","price":62,"tax":20.2},"baz":{"name":"Baz","description":None,"price":50.2,"tax":10.5,"tags":[]},}@app.get("/items/{item_id}",response_model=Item,response_model_exclude_unset=True)asyncdefread_item(item_id:str):returnitems[item_id]
  • description: Union[str, None] = None (orstr | None = None in Python 3.10) has a default ofNone.
  • tax: float = 10.5 has a default of10.5.
  • tags: List[str] = [] has a default of an empty list:[].

but you might want to omit them from the result if they were not actually stored.

For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.

Use theresponse_model_exclude_unset parameter

You can set thepath operation decorator parameterresponse_model_exclude_unset=True:

fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float=10.5tags:list[str]=[]items={"foo":{"name":"Foo","price":50.2},"bar":{"name":"Bar","description":"The bartenders","price":62,"tax":20.2},"baz":{"name":"Baz","description":None,"price":50.2,"tax":10.5,"tags":[]},}@app.get("/items/{item_id}",response_model=Item,response_model_exclude_unset=True)asyncdefread_item(item_id:str):returnitems[item_id]

and those default values won't be included in the response, only the values actually set.

So, if you send a request to thatpath operation for the item with IDfoo, the response (not including default values) will be:

{"name":"Foo","price":50.2}

Info

You can also use:

  • response_model_exclude_defaults=True
  • response_model_exclude_none=True

as described inthe Pydantic docs forexclude_defaults andexclude_none.

Data with values for fields with defaults

But if your data has values for the model's fields with default values, like the item with IDbar:

{"name":"Bar","description":"The bartenders","price":62,"tax":20.2}

they will be included in the response.

Data with the same values as the defaults

If the data has the same values as the default ones, like the item with IDbaz:

{"name":"Baz","description":None,"price":50.2,"tax":10.5,"tags":[]}

FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even thoughdescription,tax, andtags have the same values as the defaults, they were set explicitly (instead of taken from the defaults).

So, they will be included in the JSON response.

Tip

Notice that the default values can be anything, not onlyNone.

They can be a list ([]), afloat of10.5, etc.

response_model_include andresponse_model_exclude

You can also use thepath operation decorator parametersresponse_model_include andresponse_model_exclude.

They take aset ofstr with the name of the attributes to include (omitting the rest) or to exclude (including the rest).

This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output.

Tip

But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.

This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you useresponse_model_include orresponse_model_exclude to omit some attributes.

This also applies toresponse_model_by_alias that works similarly.

fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float=10.5items={"foo":{"name":"Foo","price":50.2},"bar":{"name":"Bar","description":"The Bar fighters","price":62,"tax":20.2},"baz":{"name":"Baz","description":"There goes my baz","price":50.2,"tax":10.5,},}@app.get("/items/{item_id}/name",response_model=Item,response_model_include={"name","description"},)asyncdefread_item_name(item_id:str):returnitems[item_id]@app.get("/items/{item_id}/public",response_model=Item,response_model_exclude={"tax"})asyncdefread_item_public_data(item_id:str):returnitems[item_id]

Tip

The syntax{"name", "description"} creates aset with those two values.

It is equivalent toset(["name", "description"]).

Usinglists instead ofsets

If you forget to use aset and use alist ortuple instead, FastAPI will still convert it to aset and it will work correctly:

fromfastapiimportFastAPIfrompydanticimportBaseModelapp=FastAPI()classItem(BaseModel):name:strdescription:str|None=Noneprice:floattax:float=10.5items={"foo":{"name":"Foo","price":50.2},"bar":{"name":"Bar","description":"The Bar fighters","price":62,"tax":20.2},"baz":{"name":"Baz","description":"There goes my baz","price":50.2,"tax":10.5,},}@app.get("/items/{item_id}/name",response_model=Item,response_model_include=["name","description"],)asyncdefread_item_name(item_id:str):returnitems[item_id]@app.get("/items/{item_id}/public",response_model=Item,response_model_exclude=["tax"])asyncdefread_item_public_data(item_id:str):returnitems[item_id]

Recap

Use thepath operation decorator's parameterresponse_model to define response models and especially to ensure private data is filtered out.

Useresponse_model_exclude_unset to return only the values explicitly set.


[8]ページ先頭

©2009-2026 Movatter.jp