3

I have an endpoint which returns a Pydantic object. However, I would like a response code other than 200 in some cases (for example if my service in not healthy). How can I achieve that with FastAPI?

class ServiceHealth(BaseModel):
    http_ok: bool = True
    database_ok: bool = False

    def is_everything_ok(self) -> bool:
        return self.http_ok and self.database_ok

@router.get("/health")
def health() -> ServiceHealth:
    return ServiceHealth()
3
  • Did you check fastapi.tiangolo.com/tutorial/handling-errors/… ? Something like: raise HTTPException(status_code=404, detail="Service not found.") can be useful for your intent ? Commented Mar 28, 2023 at 14:38
  • 1
    Hum, I still would like to return a Pydantic object. Commented Mar 28, 2023 at 15:38
  • Future readers might want to have a look at the last section of this answer Commented Jan 6, 2024 at 12:47

2 Answers 2

2

You can return a Response Directly.

For example, you can use JSONResponse and set the status manually:

@router.get("/health")
async def health() -> ServiceHealth:
    response = ServiceHealth()
    
    if response.is_everything_ok():
        return JSONResponse(content=response.dict(), status_code=200)
    return JSONResponse(content=response.dict(), status_code=500)

Also, there is a handy status.py which you can import from fastapi that contains all the available status codes:

from fastapi import status

@router.get("/health")
async def health() -> ServiceHealth:
    response = ServiceHealth()
    
    if response.is_everything_ok():
        return JSONResponse(content=response.dict(), status_code=status.HTTP_200_OK)
    return JSONResponse(content=response.dict(), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
Sign up to request clarification or add additional context in comments.

6 Comments

When returning a JSONResponse(content=some_dict, ....), please make sure that all objects in the dictionary that is being returned are JSON-serializable objects, otherwise a TypeError: Object of type ... is not JSON serializable would be raised. In that case, one should either convert such objects to a type that is JSON-serializable (e.g., str) on their own, or use FastAPI's jsonable_encoder() function that would automatically do this (see this answer for more details and examples).
@Chris Yes, that is correct! Another thing you can do when you have a Pydantic object (with datetimes for example, that are not JSON serializable) in order to make sure they will be serialized correctly is to use the following combo: json.load(pydantic_instance.json())
I wouldn't do that. Please have a look at the linked answer above, as well as this answer (see Option 1) to find out the reason as to why. Simply, you are converting the model instance into JSON, then the JSON string/object into dictionary, and finally, once again into JSON, since JSONResponse will use json.dumps() behind the scenes (as explained in the links provided above).
You could instead use: return Response(model.json(), media_type='application/json', status=status.HTTP_200_OK). See the linked answers above for more details.
as a side note,status is now status_code for 0.104.1
|
1

Just specify the status_code keyword-argument, when initializing your APIRoute. It is passed along by all route decorators as far as I know, including APIRouter.get.

from fastapi import FastAPI
from pydantic import BaseModel


class ServiceHealth(BaseModel):
    http_ok: bool = True
    database_ok: bool = False


api = FastAPI()


@api.get("/health", status_code=299)
def health() -> ServiceHealth:
    return ServiceHealth()

Performing a GET on that route still returns the expected JSON {"http_ok":true,"database_ok":false} with the HTTP status code 299 (just to demonstrate, not a "real" status code).

There are a few restrictions that FastAPI places on that argument. Notably, you cannot return a body, if you define a 304 status code or any informational (1xx) status code.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.