Response model
You can declare the model used for the response with the parameter response_model
in any of the path operations:
@app.get()
@app.post()
@app.put()
@app.delete()
- etc.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item
Note
Notice that response_model
is a parameter of the "decorator" method (get
, post
, etc). Not of your path operation function, like all the parameters and body.
It receives the same type you would declare for a Pydantic model attribute, so, it can be a Pydantic model, but it can also be, e.g. a list
of Pydantic models, like List[Item]
.
FastAPI will use this response_model
to:
- Convert the output data to its type declaration.
- Validate the data.
- Add a JSON Schema for the response, in the OpenAPI path operation.
- Will be used by the automatic documentation systems.
But most importantly:
- Will limit the output data to that of the model. We'll see how that's important below.
"Technical Details"
The response model is declared in this parameter instead of as a function return type annotation, because the path function may not actually return that response model but rather return a dict
, database object or some other model, and then use the response_model
to perform the field limiting and serialization.
Returning the same input data
Here we are declaring a UserIn
model, it will contain a plaintext password:
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
# Don't do this in production!
@app.post("/user/", response_model=UserIn)
async def create_user(user: UserIn):
return user
Info
To use EmailStr
, first install email_validator
.
E.g. pip install email-validator
or pip install pydantic[email]
.
And we are using this model to declare our input and the same model to declare our output:
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
# Don't do this in production!
@app.post("/user/", response_model=UserIn)
async def create_user(user: UserIn):
return user
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 the user themself is sending the password.
But if we use the same model for another path operation, we could be sending our user's passwords to every client.
Warning
Never store the plain password of a user or send it in a response.
Add an output model
We can instead create an input model with the plaintext password and an output model without it:
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn):
return user
Here, even though our path operation function is returning the same input user that contains the password. We declared the response_model
to be our model UserOut
, that doesn't include the password.
So, FastAPI will take care of filtering out all the data that is not declared in the output model (using Pydantic).
You can even specify a list of responsemodels by using list[]
:
@app.get("/user/", response_model=list[UserOut])
async def get_user():
return users