I have a route that simply fetches objects from a database and returns a list of them. They are instances of a pydantic model. However, when such an instance contains a property whose value is nan, fastapi throws an error.
ValueError: Out of range float values are not JSON compliant
I was able to track it down to the starlette.response.JSONResponse class, that under the hood calls json.dumps with the argument allow_nan=False.
The result is that if a value is nan, the json dump fails here. However, this passes the jsonable_encoder silently, as it returns objects of type float as is.
May I suggest to implement a check whether a float is nan and transform it to something else, maybe None? I am happy to send a PR if you think it's worthwhile.
class Model(pydantic.BaseModel):
value: float
app.get('/this')
def get_this():
x = Model(value=float('nan'))
return x
Linux 42-Ubuntu SMP Wed Aug 7 15:17:54 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Python 3.7.3
fastapi 0.31.0
starlette 0.12.0
I ran into the exact same issue and solved it by replacing the built-in JSON library with a custom one... (orjson worked well for my needs)
class ORJSONResponse(JSONResponse):
media_type = "application/json"
def render(self, content: typing.Any) -> bytes:
return orjson.dumps(content)
...and then overriding the default response class.
app = FastAPI(default_response_class=ORJSONResponse)
Keep in mind the default_response_class option was added in FastAPI 0.39.0
In addition to @zbarnett 's great suggestion, in pydantic v1 (which is coming very soon) there will be better support for specifying a custom encoder for your pydantic models. So that may (eventually) help from another angle.
Thanks, @zbarnett . Your solution is actually much nicer then what I had in mind.
In the end, we moved the NaN checks to the data ingestion part outside of fastapi.
Thanks for the help here everyone! :clap: :bow:
Thanks for reporting back and closing the issue @henninglebbaeus :+1:
I ran into the exact same issue and solved it by replacing the built-in JSON library with a custom one... (orjson worked well for my needs)
class ORJSONResponse(JSONResponse): media_type = "application/json" def render(self, content: typing.Any) -> bytes: return orjson.dumps(content)...and then overriding the default response class.
app = FastAPI(default_response_class=ORJSONResponse)Keep in mind the default_response_class option was added in FastAPI 0.39.0
good solution and ths.
Most helpful comment
I ran into the exact same issue and solved it by replacing the built-in JSON library with a custom one... (orjson worked well for my needs)
...and then overriding the default response class.
Keep in mind the default_response_class option was added in FastAPI 0.39.0