Skip to main content

fastapi-rfc9457

PyPI

Typed, batteries-included RFC 9457 "Problem Details for HTTP APIs" for FastAPI & Pydantic.

Define an error once - it serializes as application/problem+json, documents itself in OpenAPI, and parses back into a typed exception on the client.

from fastapi import FastAPI

from fastapi_rfc9457 import Problem
from fastapi_rfc9457.server import add_problem_handlers, get_problem_docs_router, problems


class OutOfCredit(Problem):
    """The account does not have enough credit."""
    title = "Out of Credit"
    status = 403
    balance: int            # typed extension members, checked at the raise site
    accounts: list[str]


class AccountSuspended(Problem):
    """The account is suspended and cannot be charged."""
    title = "Account Suspended"
    status = 403


app = FastAPI()
add_problem_handlers(app)                                          # handlers + problem+json OpenAPI
app.include_router(get_problem_docs_router(), prefix="/problems")  # dereferenceable type URIs


@app.get("/charge", responses=problems(OutOfCredit, AccountSuspended))
async def charge() -> dict:
    raise OutOfCredit(detail="Not enough credit.", balance=30, accounts=["/acct/12"])

Accurate OpenAPI, for free

One route can declare several failure modes. Distinct statuses get their own response; same-status problems become a oneOf union you flip through in Swagger's Examples dropdown — all under application/problem+json.

Swagger error responses with a problem+json examples dropdown

Dereferenceable type URIs

Mount the docs router and every problem type resolves to a live page listing its typed extension members.

The type is derived from the docs-router mount, not hard-coded: mount at prefix="/problems" and OutOfCredit emits and serves /problems/out-of-credit. Change the prefix and bodies, OpenAPI, and doc pages move together. Set type explicitly to emit a literal URI instead.

Problem type documentation page

Typed exceptions on the client

Client-side, the package can parse application/problem+json back into typed problems the server raised.

import httpx
from fastapi_rfc9457 import Problem, httpx_raise_hook


class OutOfCredit(Problem):      # the type the server declares, shared or re-stated
    title = "Out of Credit"
    status = 403
    balance: int

with httpx.Client(
    base_url="http://localhost:8000",
    event_hooks={
        "response": [httpx_raise_hook()]
    }) as client:
    try:
        client.get("/charge")
    except OutOfCredit as exc:
        print(exc.balance)       # extension members round-trip back as typed attributes

Prefer to parse explicitly? parse_problem(response) returns the typed Problem (or a generic ProblemDetail for an unknown type), and raise_for_problem(response) raises it.

Comparison with native FastAPI

see Handling Errors

class OutOfCreditError(Exception):
    def __init__(self, detail: str, balance: int) -> None:
        self.detail, self.balance = detail, balance

@app.exception_handler(OutOfCreditError)
async def _(request: Request, exc: OutOfCreditError) -> JSONResponse:
    return JSONResponse({"detail": exc.detail, "balance": exc.balance}, 403)

class OutOfCreditBody(BaseModel):
    detail: str
    balance: int

@app.get("/charge", responses={403: {"model": OutOfCreditBody}})
async def charge(token: str | None = None) -> dict:
    if token is None:
        raise HTTPException(401, "Log in first")
    raise OutOfCreditError("Not enough credit", balance=30)
# fastapi-rfc9457 enables a single class for the exception, the body, and the OpenAPI schema
from fastapi_rfc9457 import NotAuthenticated, Problem  # NotAuthenticated ships built in
from fastapi_rfc9457.server import problems

class OutOfCredit(Problem):
    title = "Out of Credit"
    status = 403
    balance: int

@app.get("/charge", responses=problems(NotAuthenticated, OutOfCredit))
async def charge(token: str | None = None) -> dict:
    if token is None:
        raise NotAuthenticated(detail="Log in first")
    raise OutOfCredit(detail="Not enough credit", balance=30)
    # → 403 application/problem+json
    #   {"type": "/problems/out-of-credit", "title": "Out of Credit",
    #    "status": 403, "detail": "Not enough credit", "balance": 30}
Plain FastAPI fastapi-rfc9457
Typed extra fields in the body and OpenAPI exception + handler + model, by hand
Errors documented as application/problem+json ❌ (application/json)
Same-status errors as oneOf + Examples dropdown
Dereferenceable type URIs with doc pages

Similar projects

Install

uv add fastapi-rfc9457[server]   # FastAPI apps: handlers, OpenAPI, docs router
uv add fastapi-rfc9457           # lean client: author + parse problems, Pydantic only

Example

cd example && uv run uvicorn main:app --reload   # then open localhost:8000/docs

See example/ for the full runnable app, and example/client.py for the httpx hook (uv add fastapi-rfc9457 httpx) that raises those problems back as typed exceptions on the consumer side.

Notes

  • Replaces FastAPI's default 422 body with application/problem+json.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_rfc9457-0.2.1.tar.gz (32.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_rfc9457-0.2.1-py3-none-any.whl (23.9 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_rfc9457-0.2.1.tar.gz.

File metadata

  • Download URL: fastapi_rfc9457-0.2.1.tar.gz
  • Upload date:
  • Size: 32.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastapi_rfc9457-0.2.1.tar.gz
Algorithm Hash digest
SHA256 171cfd9ecd981c5442f8606a60b8a48d5d559117d43bdda34e513942d9ccc4fe
MD5 15e5e0eb217f14e83ec97dc3da2be618
BLAKE2b-256 756ef3c4815acefb733283ff0c7b5d0ebb64ce059cefce0ef6969005131f5019

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_rfc9457-0.2.1.tar.gz:

Publisher: publish.yaml on PythonFZ/fastapi-rfc9457

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastapi_rfc9457-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: fastapi_rfc9457-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 23.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastapi_rfc9457-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 caceb4f4d1c4c1237cf2e34b7543b12bf91a169287b90abf0042798709f3aa35
MD5 ea7fc5e45d4f67a7a5f19a41f9cb38e9
BLAKE2b-256 088c372e8f9c92efa7fbe3cc9b77aa19762abb795ff1dfb50f425847f3cd0e24

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_rfc9457-0.2.1-py3-none-any.whl:

Publisher: publish.yaml on PythonFZ/fastapi-rfc9457

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 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