Skip to main content

FastAPI Easy Responses

A simple package to easily handle and document all HTTP exceptions and other errors in a FastAPI app.

Installation

uv add fastapi-easy-responses
pip install fastapi-easy-responses

Usage

# main
from fastapi_easy_responses import register_custom_exceptions
app = FastAPI()
register_custom_exceptions(app) # 1.

# exceptions
from fastapi_easy_responses import CustomAppException
class DuplicateItemError(CustomAppException): # 2.
    status_code = 409
    description = "An item with this name already exists."

# crud
async def create_item(session: AsyncSession, item: Item) -> Item:
    try:
        session.add(item)
        await session.commit()
        await session.refresh(item)
        return item
    except IntegrityError as e:
        await session.rollback()
        raise DuplicateItemError() from e # 3.

# router
from fastapi_easy_responses import get_responses
@router.post(
    "",
    response_model=ItemRead,
    status_code=201,
    responses=get_responses(DuplicateItemError), # 4.
)
async def create_item_endpoint(item: ItemCreate, session: AsyncSession):
    db_item = Item.model_validate(item)
    return await create_item(session, db_item) # 5.

This gives you a centralized, more consistent and easier-to-maintain exception handling and documentation.

Note the following:

  1. Call register_custom_exceptions(app) to activate the centralized exception handler for CustomAppExceptions.
  2. Inherit your exception class from CustomAppException, and provide the required status_code and description as class variables: as such, these are static per exception class, not dynamic per raise.
  3. Raise your exception in any operation.
  4. Use the same exception class to generate the OpenAPI documentation. No magic numbers and strings needed, so you have proper autocomplete.
  5. No need to manually catch and convert your exception to HTTPException, the centralized exception handler does it for you. Or more precisely, it returns the same JSONResponse as the default exception handler for HTTPException would.

This package doesn't introduce new response schemas or custom error codes, so adoption is easy: the exact same response is returned as with the pure FastAPI implementation, so you don't have to worry about rewriting your other services or frontend.

Result

If you open the documentation, you'll see the following:

Documentation sample

And if you try it out, you'll see the actual response matches the documentation as expected:

Response sample

Dynamic detail

By default the response will contain the static description of the exception, which is usually enough.

But you can also provide the detail parameter to the base CustomAppException class, which will be used in the response instead of the static description if present.

Example:

class ItemNotFoundError(CustomAppException):
    status_code = 404
    description = "Item not found"

    def __init__(self, item_id: int):
        super().__init__(detail=f"Item with ID {item_id} not found")

# Raise with dynamic detail
raise ItemNotFoundError(123)

# Use in documentation as usual
@router.get(
    "/{item_id}", response_model=ItemRead, responses=get_responses(ItemNotFoundError)
)

In this case, the documentation will still show the static description:

Documentation sample

But the actual response will contain the dynamic detail:

Response sample

Headers

You can also include custom headers in your responses by providing the headers parameter to the base CustomAppException class. This allows you to specify headers that should be included in the response when the exception is raised.

For documentation purposes, you can also define a header_descriptions attribute in your exception class. You can use anything here that you would normally use directly in the responses parameter of your route decorator.

Example:

class UnauthorizedError(CustomAppException):
    status_code = status.HTTP_401_UNAUTHORIZED
    description = "Invalid credentials"
    header_descriptions = {
        "WWW-Authenticate": {
            "description": "Available authentication methods",
            "schema": {"type": "string"},
        }
    }

    def __init__(self):
        super().__init__(headers={"WWW-Authenticate": "Bearer"})

# Raise where appropriate
raise UnauthorizedError()

# Use in documentation as usual
@router.get("/me", responses=get_responses(UnauthorizedError))

In this case, the documentation will show the given header descriptions:

Documentation sample

And the actual response will contain the given headers:

Response sample

Why

For comparison, this is something like what you would usually do in a FastAPI app.

# exceptions
class DuplicateItemError(ValueError):
    pass

# crud
async def create_item(session: AsyncSession, item: Item) -> Item:
    try:
        session.add(item)
        await session.commit()
        await session.refresh(item)
        return item
    except IntegrityError as e:
        await session.rollback()
        raise DuplicateItemError("An item with this name already exists.") from e

# router
@router.post(
    "",
    response_model=ItemRead,
    status_code=201,
    responses={
        409: {"model": Message, "description": "An item with this name already exists."}
    }
)
async def create_item_endpoint(item: ItemCreate, session: AsyncSession):
    try:
        db_item = Item.model_validate(item)
        return await create_item(session, db_item)
    except DuplicateItemError as e:
        raise HTTPException(status_code=409, detail=str(e)) from e

Which means

  • You have to manually maintain all the thrown exception to HTTPException mappings. If you forget it, your unhandled exception will raise a generic internal server error.
  • It also requires additional manual work to provide consistent documentation.

Similar packages

There are similar packages with similar purpose, like

This package is simpler for basic use-cases, which may or may not be what you want. This package doesn't introduce new response schemas or custom error codes, doesn't provide logging (yet); but gives you the least amount of code you have to write to achieve the same functionality.

License

MIT

Metadata

Release files for fastapi-easy-responses 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-easy-responses 0.2.2
File Size Uploaded
fastapi_easy_responses-0.2.2.tar.gz 4.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-easy-responses 0.2.2
File Interpreter ABI Platform
fastapi_easy_responses-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 10.1 kB

Release files / fastapi_easy_responses-0.2.2.tar.gz

Download URL fastapi_easy_responses-0.2.2.tar.gz
Size 4.6 kB
Tags Source
SHA-256 checksum
How to use checksums
138c86cc3467e7acf5a8d1c3a61e7ef4b9aa0728184f2cda1024bc2a8882159a
BLAKE2b-256 checksum
How to use checksums
cfcf08c885c0eadda65d291436f2746d91f315549bd844dfe2870fed7a295360
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / fastapi_easy_responses-0.2.2-py3-none-any.whl

Download URL fastapi_easy_responses-0.2.2-py3-none-any.whl
Size 5.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
47023d609b542cd08383e6ddce20ff6a7baa8d4fe04b8c3bd001ac707a0b3247
BLAKE2b-256 checksum
How to use checksums
e961bfe0c9e0c404d9a0ca6e88121250a0d1973b9cc47cec229d6d3e91353a38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.2.3

2 release files

This release

0.2.2 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page