Typed error responses for FastAPI: Literal error codes, discriminated unions in OpenAPI, single source of truth
Project description
fastapi-typed-errors
English · Русский
Typed HTTP errors for FastAPI — exact Literal codes in OpenAPI, discriminated oneOf unions on the code field, and a single source of truth: the error class itself.
In plain FastAPI, raise HTTPException(404, "...") buries the error code in a string — clients can't switch on it, OpenAPI has no code type or body schema, and you hand-maintain responses={} on every route. This package makes an error a class: its HTTP status, machine-readable code and response model are declared once and derived automatically, so your error contract is typed, self-documenting, and verifiable in CI.
class NotFoundError(BaseError[Literal[ErrorCode.NOT_FOUND]]):
http_status = HTTPStatus.NOT_FOUND
@router.get("/items/{item_id}")
def get_item(item_id: int) -> Annotated[Item, Raises[NotFoundError]]:
if item_id == 0:
raise NotFoundError("No item")
return Item(item_id=item_id)
The response body is always {"code": "NOT_FOUND", "detail": "No item"}, and OpenAPI gets a 404 with the exact Literal["NOT_FOUND"] code and body model — no manual responses.
Install
pip install fastapi-typed-errors # core + decorator
pip install "fastapi-typed-errors[cli]" # + the CI-checker CLI
Requires Python 3.12+, FastAPI ≥ 0.115, Pydantic ≥ 2.9.
How to use it
1. Define errors and register the one handler.
from fastapi import FastAPI
from fastapi_typed_errors import BaseError, handle_base_error
app = FastAPI()
app.add_exception_handler(BaseError, handle_base_error)
2. Declare errors — pick your level of magic:
# a) by hand (core only) — write responses yourself
@app.get("/x", responses={404: {"model": error_models(NotFoundError)}})
def a() -> Item: ...
# b) declared — the marker fills responses for you
router = with_errors(APIRouter())
@router.get("/y")
def b() -> Annotated[Item, Raises[NotFoundError, ForbiddenError]]: ...
# c) automatic — no markers at all; errors are found statically
router = with_errors(APIRouter(), auto=True)
@router.get("/z")
def c(user: Annotated[User, Depends(current_user)]) -> Item:
raise NotFoundError("...") # auto -> 404, plus whatever current_user raises
3. Verify the contract in CI. check_raises compares what each route declares against what it can actually raise — in the endpoint and its whole dependency tree:
def test_error_contracts() -> None:
assert check_raises(app).ok
Or as a command: fastapi-typed-errors check app.main:app (exit 0/1/2).
Why it's cool
- Exact types in OpenAPI — a precise
Literalcode per status; several errors on one status become a discriminatedoneOfunion, so Swagger UI shows a variant picker by code. - Single source of truth — status, code and model declared once; the metaclass derives the rest.
- Zero-boilerplate
responses— via theRaisesmarker or fully automaticauto=True. - Static contract checking —
check_raisescatches a raise you forgot to declare (or a dead declaration) before it ships. - Non-invasive —
with_errorspatches the router in place and preserves object identity, soinclude_router, websockets and app-level decorators keep working natively. - Rigorous — fully typed (
py.typed), 100% branch-covered, checked withruff+tyat max strictness.
Documentation
📖 Full documentation — guide, customization (custom envelopes, ABC, bare-string codes), limitations, and an auto-generated API reference. Available in English and Russian.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_typed_errors-1.0.1.tar.gz.
File metadata
- Download URL: fastapi_typed_errors-1.0.1.tar.gz
- Upload date:
- Size: 21.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f7e5a51f42f4daaa45893cdd1ae4004c348ffeb8f9bb60e56e8329dc4f3f356
|
|
| MD5 |
d3cb7bf2a6e69d42509d22d20e6bc7ae
|
|
| BLAKE2b-256 |
d27cc705bcef7cd6dd7d6ee7e6d6b3006adb4e4777317400b2b47ab30d130622
|
File details
Details for the file fastapi_typed_errors-1.0.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_typed_errors-1.0.1-py3-none-any.whl
- Upload date:
- Size: 27.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fd6112421a99e6738357ac9aeb573954154ffe2479e0f42062606734c8df9e8
|
|
| MD5 |
05db8727d31f9e148a68adf5d277426a
|
|
| BLAKE2b-256 |
6bef30bd569c3980d21970dc98033df13d3ea8cf8fa564d022483ed04b85d4a2
|