I am using the fastAPI to build API.
It allows to generate API docs automatically.
It also shows list of possible response codes. By default it shows only a success code and 422 code.
But in my app there are more codes allowed. For example 404, 401, 403 etc.
All these exceptions are not inside a code of en endpoint. It is inside a package
@app.get("/items/{item_id}")
async def read_item(item_id: str):
data, error, message = do_something()
if error != 0:
raise HTTPException(status_code=error, detail=message)
return data
How can i notify the decorator about all possible codes? is it possible?
You can use APIRouter
in fastapi to define routes and customized responses:
from fastapi import HTTPException, APIRouter
router = APIRouter()
@router.get("/items/{item_id}")
async def read_item(item_id: str):
data, error, message = do_something()
if error != 0:
raise HTTPException(status_code=error, detail=message)
return data
then you can define your desired responses:
read_item.responses = {
200: {"description": "Successful response"},
404: {"description": "Item not found"},
401: {"description": "Unauthorized access"},
403: {"description": "Forbidden access"}
}
Another approach is to define response in @router.get()
:
@router.get("/items/{item_id}",
description="Retrieves an item by its ID.",
response_model=Item,
responses={
200: {"description": "Successful response"},
404: {"description": "Item not found"},
401: {"description": "Unauthorized access"},
403: {"description": "Forbidden access"}
}
)
1
You can create a global exception handler for specific HTTP status codes and register it with FastAPI. This approach allows you to document the responses in a more centralized manner, especially when these status codes are frequently raised from within a package. This is the sample pseodo code sample that I used for my existing projects.
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from typing import Optional
app = FastAPI()
# Custom Exception for 404 - Not Found
class ItemNotFoundException(Exception):
def __init__(self, item_id: str):
self.item_id = item_id
# Custom Exception for 403 - Forbidden
class ForbiddenAccessException(Exception):
def __init__(self, item_id: str):
self.item_id = item_id
# Global handler for HTTPException (e.g., 401 Unauthorized, 500 Internal Server Error)
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"message": exc.detail},
)
# Global handler for ItemNotFoundException (404 Not Found)
@app.exception_handler(ItemNotFoundException)
async def item_not_found_exception_handler(request: Request, exc: ItemNotFoundException):
return JSONResponse(
status_code=404,
content={"message": f"Item with ID {exc.item_id} not found"},
)
# Global handler for ForbiddenAccessException (403 Forbidden)
@app.exception_handler(ForbiddenAccessException)
async def forbidden_access_exception_handler(request: Request, exc: ForbiddenAccessException):
return JSONResponse(
status_code=403,
content={"message": f"Access to item with ID {exc.item_id} is forbidden"},
)
# Global handler for ValueError (400 Bad Request)
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"message": str(exc)},
)
# Global catch-all handler for uncaught exceptions (500 Internal Server Error)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"message": "An internal server error occurred"},
)
# Sample endpoint with various exceptions
@app.get("/items/{item_id}", responses={
200: {"description": "Successful Response"},
400: {"description": "Bad Request"},
401: {"description": "Unauthorized"},
403: {"description": "Forbidden"},
404: {"description": "Item not found"},
500: {"description": "Internal Server Error"},
})
async def read_item(item_id: str, authorized: Optional[bool] = True):
# Example logic that could trigger different exceptions
if item_id == "notfound":
raise ItemNotFoundException(item_id) # Triggers 404
if item_id == "forbidden":
raise ForbiddenAccessException(item_id) # Triggers 403
if item_id == "badrequest":
raise ValueError("Invalid request") # Triggers 400
if not authorized:
raise HTTPException(status_code=401, detail="Unauthorized access") # Triggers 401
if item_id == "error":
raise RuntimeError("An unexpected error occurred") # Triggers 500
# If no exception, return the item data
return {"item_id": item_id, "status": "Success"}
Also, specify responses in the endpoint.So in this case, when you
visit the /docs page, the API documentation will show the possible
status codes (200, 404, 403, 400, 500), making it clear to users that
the endpoint could return these status codes.