GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - feodor-ra/fastapi-typed-errors: Typed HTTP errors for FastAPI: Literal error codes, discriminated oneOf unions in OpenAPI, single source of truth — the error class.

BranchesTags
Open more actions menu

Latest commit

 

History

23 Commits
23 Commits

Folders and files

NameName
Last commit message
Last commit date
.github
.github
 
 
docs
docs
 
 
overrides
overrides
 
 
scripts
scripts
 
 
src/fastapi_typed_errors
src/fastapi_typed_errors
 
 
tests
tests
 
 
.gitignore
.gitignore
 
 
.python-version
.python-version
 
 
CLAUDE.md
CLAUDE.md
 
 
Justfile
Justfile
 
 
LICENSE
LICENSE
 
 
README.md
README.md
 
 
README.ru.md
README.ru.md
 
 
context7.json
context7.json
 
 
mkdocs.yml
mkdocs.yml
 
 
pyproject.toml
pyproject.toml
 
 
uv.lock
uv.lock
 
 

Repository files navigation

fastapi-typed-errors

English · Русский

PyPI - Version PyPI - Python Version PyPI - Status PyPI - Downloads FastAPI Pydantic v2

GitHub Release GitHub Repo stars GitHub last commit CI Coverage Status Docs

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 Literal code per status; several errors on one status become a discriminated oneOf union, 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 the Raises marker or fully automatic auto=True.
  • Static contract checking — check_raises catches a raise you forgot to declare (or a dead declaration) before it ships.
  • Non-invasive — with_errors patches the router in place and preserves object identity, so include_router, websockets and app-level decorators keep working natively.
  • Rigorous — fully typed (py.typed), 100% branch-covered, checked with ruff + ty at 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.


uv Ruff ty pytest Material for MkDocs Conventional Commits Semantic Versions

GitHub License

About

Typed HTTP errors for FastAPI: Literal error codes, discriminated oneOf unions in OpenAPI, single source of truth — the error class.

feodor-ra.github.io/fastapi-typed-errors/

Topics

apierror-handlingexception-handlingfastapihttp-errorsopenapipydanticpythonrest-apiswaggertype-safetytyping

Resources

Readme
MIT license
Activity

Stars

4 stars

Watchers

0 watching

Forks

0 forks
Report repository

Releases

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.