I am somewhat confused about how to handle exceptions within my FastAPI code.
I can show a very simple example to illustrate the point.
The design of my system consists of two parts:
- There is a FastAPI webserver interface which wraps some core logic
- There is a class which contains all of the core logic, let’s call it
KeyValueStore
for this example
The core logic can raise exceptions. I am not sure how I should be handling these in the FastAPI layer.
Here is a very simple example, it is a simplified version of what my code actually does.
Let’s say that the core logic is as simple as a data structure which stores key-value pairs. (This is actually pretty close to what is actually going on, only in my actual logic there are multiple levels of key.)
class KeyValueStore():
def __init__(self):
self.data = {}
def put(self, key, value):
if key in self.data:
raise RuntimeError(f'duplicate key {key}')
self.data[key] = value
def get(self, key):
# doesn't really matter what the implementation is
As you can see, if KeyValueStore.put()
is called with a key which has previously been used, this results in an error of time RuntimeError
being raised.
This is caught in the FastAPI layer. The code looks something like this:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi import status
from pydantic import BaseModel
class FastAPI_KeyValuePair(BaseModel):
key: str
value: str
class FastAPI_ReturnStatus(BaseModel):
status: str
message: str|None = None
@app.post('/put_key_value')
def put_key_value(key_value_pair: FastAPI_KeyValuePair):
try:
key_value_store.put(
key_value_pair.key,
key_value_pair.value,
)
return FastAPI_ReturnStatus(status='success', message=None)
except RuntimeError as error:
return JSONResponse(
status_code=status.HTTP_409_CONFLICT,
content=FastAPI_ReturnStatus(
status='error',
message=str(error),
)
)
- In this particular case, I think that I want to return a
HTTP_409_CONFLICT
status code - I wasn’t sure how to do this while also returning some JSON body
A normal return statement looks something like this:
return FastAPI_ReturnStatus(status='success', message=None)
If I understand correctly, FastAPI will serialize this type to json using json.dumps
. This serialized content will be returned as the response body. In addition, the status code is automatically set to 200.
What I wanted to do was set this status code to something else, such as 409. I couldn’t find a way to do that, except by using JSONResponse
. This may not be the right method, I am unsure.
What actually happens with this code is that I get an exception:
TypeError: Object of type FastAPI_ReturnStatus is not JSON serializable
This confuses me, because this type is JSON serializable. It must be, because the line
return FastAPI_ReturnStatus(status='success', message=None)
works!
However this code,
return JSONResponse(
status_code=status.HTTP_409_CONFLICT,
content=FastAPI_ReturnStatus(
status='error',
message=str(error),
)
)
does not work, and it is this block of code which causes the exception.
So why is FastAPI_ReturnStatus
not serializable in this context, but is serializable if the return type is FastAPI_ReturnStatus
(not nested in some other object).