I have the following route:
# 201 is the response from a creation
# 409 if it already exists
# The server SHOULD generate a payload that includes enough information for a user to recognize the source of the conflict.
@app.post("/users", status_code=status.HTTP_201_CREATED, response_model=schemas.UserResponse)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
# hash the password -- user.password
user.password = utils.hash(user.password)
new_user = models.User(**user.dict()) # get or create? -- https://stackoverflow.com/a/6078058/651174
db.add(new_user)
try:
db.commit()
except sqlalchemy.exc.IntegrityError:
db.rollback()
existing_user = db.query(models.User).filter(models.User.email == user.email).first()
raise HTTPException(status.HTTP_409_CONFLICT, detail=existing_user)
db.refresh(new_user) # the same as doing `RETURNING *`
return new_user
The user
object returns and is encoded fine when it doesn't hit the exception. However, when it does go through the exception, and executes:
raise HTTPException(status.HTTP_409_CONFLICT, detail=existing_user)
I get the following error:
TypeError: Object of type User is not JSON serializable
Is there a way to basically encode all responses using the model I specify in FastAPI?
The reason that your new_user
object is returned successfully when the request doesn't hit the exception is that (as explained in the documentation):
By default, FastAPI would automatically convert that return value to
JSON
using thejsonable_encoder
explained in JSON Compatible Encoder. Then, behind the scenes, it would put that JSON-compatible data (e.g. adict
) inside of aJSONResponse
that would be used to send the response to the client.
As explained in this answer in more detail, when returning a value from an endpoint, FastAPI will first convert the value into JSON-compatible data using the jsonable_encoder
, thus ensuring that any objects that are not serializable, such as datetime
objects, are converted to a str
. Hence, avoiding any potential errors, such as:
TypeError: Object of type User is not JSON serializable
when FastAPI will later put that data into a JSONResponse
, which uses the standard json.dumps()
function to convert the data into JSON (see the linked answer above for more details and references).
However, when raising an HTTPException
, which would actually return a JSONResponse
directly without using the jsonable_encoder
first to ensure that the data are JSON-compatible (you can check that through http_exception_handler
), you would need to convert the existing_user
object/model using jsonable_encoder
by yourself, before passing it to the exception. As described in the documentation:
You cannot put a Pydantic model in a
JSONResponse
without first converting it to adict
with all the data types (likedatetime
,UUID
, etc) converted to JSON-compatible types.
Hence, you should use:
from fastapi.encoders import jsonable_encoder
raise HTTPException(detail=jsonable_encoder(existing_user), status_code=status.HTTP_409_CONFLICT)