Skip to main content

FastAPI annotated api routed

Project description

Annotated FastAPI router

Этот пакет содержит улучшенный fastapi.APIRouter, который содержит шорт-каты для удобной аннотации эндпоинтов через typing.Annotated, что бы задавать значения status_code или модели ответов в responses.

Warning the English part of README.md is translated via ChatGPT and may not be accurate

This package contains an enhanced fastapi.APIRouter that includes shortcuts for annotating endpoints conveniently using typing.Annotated, allowing you to set values like status_code or response models in responses.

Примеры

Статус-код

Указывать status_code у эндпоинта можно следующим образом:

from http import HTTPStatus
from typing import Annotated

from fastapi import FastAPI
from pydantic import BaseModel

from annotated_fastapi_router import AnnotatedAPIRouter, Status


class ResponseModel(BaseModel):
    message: str


router = AnnotatedAPIRouter()


@router.get("/my_endpoint")
async def my_endpoint() -> Annotated[ResponseModel, Status[HTTPStatus.ACCEPTED]]:
    return ResponseModel(message="hello")


router.add_api_route("/my_endpoint", my_endpoint, methods=["POST"])

В результате swagger будет содержать следующую OpenAPI-схему:

GET request swagger POST request swagger

При запросе статус код так же будет проставлен в ответ:

POST response swagger

Однако если статус код будет указан явно в декораторе или при добавлении в роутер, Status в аннотации будет проигнорирован.

@router.get("/my_endpoint")
@router.get("/my_endpoint_too", status_code=HTTPStatus.CREATED)
async def my_endpoint() -> Annotated[ResponseModel, Status[HTTPStatus.ACCEPTED]]:
    return ResponseModel(message="hello")


router.add_api_route("/my_endpoint", my_endpoint, methods=["POST"])
router.add_api_route("/my_endpoint_too", my_endpoint, status_code=HTTPStatus.CREATED, methods=["POST"])

GET request swagger keep POST request swagger keep

Важно: Status принимает в себя только перечисления из HTTPStatus, на любые другие объекты будет подыматься ошибка TypeError.

Модель ответов для ошибок

Для автоматического построения моделей для responses используется тип ошибок ResponseError. В его наследниках обязательно нужно указывать переменную типа status и model.

status указывает для каких кодов ошибок нужно будет построить модель, если будет передано несколько типов ошибок для одного и того же HTTPStatus, то полученная модель будет объединять в себе все модели model.

from http import HTTPStatus
from typing import Annotated, Self

from fastapi import FastAPI
from pydantic import BaseModel

from annotated_fastapi_router import AnnotatedAPIRouter, Errors, ResponseError


class ResponseModel(BaseModel):
    message: str


class ErrorMessageModel(BaseModel):
    message: str


class OtherErrorModel(BaseModel):
    code: int


class MyError(ResponseError[ErrorMessageModel]):
    status = HTTPStatus.BAD_REQUEST
    model = ErrorMessageModel

    def __init__(self: "MyError", msg: str) -> None:
        self.message = msg


class OtherError(ResponseError[OtherErrorModel]):
    status = HTTPStatus.BAD_REQUEST
    model = OtherErrorModel

    async def entity(self: Self) -> OtherErrorModel:
        return self.model(code=self.status)


router = AnnotatedAPIRouter()


@router.get("/my_endpoint")
@router.post("/my_endpoint")
async def endpoint(how_iam: str) -> Annotated[ResponseModel, Errors[MyError, OtherError]]:
    if how_iam == "me":
        return ResponseModel(message="hello")
    if how_iam.isdigit():
        raise OtherError
    raise MyError("I don't know you")

Таким образом будет построены две модели ошибок – MyEndpointPOSTBadRequest и MyEndpointGETBadRequest.

Build error model GET Build error model POST

Имя модели генерируется из пути, методов (если указаны) роута и phrase статус-кода ошибки. Делается это потому что для fastapi<=0.95.0 требуется что бы все названия моделей (если они отличаются) имели уникальные имена. Если модель ошибки состоит только из одной модели – тогда будет использовано имя оригинальной модели (что бы избегать баг в OpenAPI-схеме с некорректным определением компонента).

Логику построения имени модели можно изменить наследованием от AnnotatedAPIRouter и переопределением метода response_model_name.

Так же для построение экземпляра модели ошибки по умолчанию использует атрибуты экземпляра ошибки (через __dict__), но если это поведение необходимо изменить можно реализовать метод entity (как в примере в класса OtherError).

Если responses уже определяется в декораторе роута или при явном добавлении в него и содержит описание для ошибки, которая указана в Annotated, то она не будет перезаписывать данные переданные в responses.

@router.get("/my_endpoint_too", responses={HTTPStatus.BAD_REQUEST: {"model": dict[str, str]}})
async def endpoint(how_iam: str) -> Annotated[ResponseModel, Errors[MyError, OtherError]]:
    if how_iam == "me":
        return ResponseModel(message="hello")
    if how_iam.isdigit():
        raise OtherError
    raise MyError("I don't know you")

Keep responses data model

Для того что бы автоматически отлавливать ошибки из эндпоинтов и указывать в ответе тот статус-код, который в указан в них, необходимо добавить в FastAPI приложение обработчик ошибок ResponseError.handler.

app.add_exception_handler(ResponseError, ResponseError.handler)

Handle MyError Handle OtherError

Examples

Status-code

To specify the status_code of the endpoint, you can do it as follows:

from http import HTTPStatus
from typing import Annotated

from fastapi import FastAPI
from pydantic import BaseModel

from annotated_fastapi_router import AnnotatedAPIRouter, Status


class ResponseModel(BaseModel):
    message: str


router = AnnotatedAPIRouter()


@router.get("/my_endpoint")
async def my_endpoint() -> Annotated[ResponseModel, Status[HTTPStatus.ACCEPTED]]:
    return ResponseModel(message="hello")


router.add_api_route("/my_endpoint", my_endpoint, methods=["POST"])

As a result, the swagger will contain the following OpenAPI schema:

GET request swagger POST request swagger

When the endpoint is requested, the status code will also be included in the response:

POST response swagger

However, if the status code is explicitly provided in the decorator or when adding it to the router, the Status in the annotation will be ignored.

@router.get("/my_endpoint")
@router.get("/my_endpoint_too", status_code=HTTPStatus.CREATED)
async def my_endpoint() -> Annotated[ResponseModel, Status[HTTPStatus.ACCEPTED]]:
    return ResponseModel(message="hello")


router.add_api_route("/my_endpoint", my_endpoint, methods=["POST"])
router.add_api_route("/my_endpoint_too", my_endpoint, status_code=HTTPStatus.CREATED, methods=["POST"])

GET request swagger keep POST request swagger keep

Important: Status only accepts enumerations from HTTPStatus; any other objects will raise a TypeError

Responses

To automatically build models for responses, the error type ResponseError is used. In its subclasses, it is necessary to specify variables of type status and model.

status indicates for which error codes the model will need to be constructed. If multiple error types are provided for the same HTTPStatus, the resulting model will combine all model models.

from http import HTTPStatus
from typing import Annotated, Self

from fastapi import FastAPI
from pydantic import BaseModel

from annotated_fastapi_router import AnnotatedAPIRouter, Errors, ResponseError


class ResponseModel(BaseModel):
    message: str


class ErrorMessageModel(BaseModel):
    message: str


class OtherErrorModel(BaseModel):
    code: int


class MyError(ResponseError[ErrorMessageModel]):
    status = HTTPStatus.BAD_REQUEST
    model = ErrorMessageModel

    def __init__(self: "MyError", msg: str) -> None:
        self.message = msg


class OtherError(ResponseError[OtherErrorModel]):
    status = HTTPStatus.BAD_REQUEST
    model = OtherErrorModel

    async def entity(self: Self) -> OtherErrorModel:
        return self.model(code=self.status)


router = AnnotatedAPIRouter()


@router.get("/my_endpoint")
@router.post("/my_endpoint")
async def endpoint(how_iam: str) -> Annotated[ResponseModel, Errors[MyError, OtherError]]:
    if how_iam == "me":
        return ResponseModel(message="hello")
    if how_iam.isdigit():
        raise OtherError
    raise MyError("I don't know you")

In this way, two error models will be built - MyEndpointPOSTBadRequest and MyEndpointGETBadRequest.

Build error model GET Build error model POST

The model name is generated from the path, methods (if specified) of the route, and the status code error phrase. This is done because for fastapi<=0.95.0, all model names (if they differ) must have unique names. If the error model consists of only one model, the name of the original model will be used (to avoid a bug in the OpenAPI schema with an incorrect component definition).

The logic for naming the model can be changed by inheriting from AnnotatedAPIRouter and overriding the response_model_name method.

Also, to build an instance of the error model by default, it uses the error instance attributes (via __dict__), but if this behavior needs to be changed, you can implement the entity method (as shown in the example in the OtherError class).

If responses are already defined in the route decorator or explicitly added to it and contain a description for an error specified in Annotated, it will not overwrite the data passed to responses.

@router.get("/my_endpoint_too", responses={HTTPStatus.BAD_REQUEST: {"model": dict[str, str]}})
async def endpoint(how_iam: str) -> Annotated[ResponseModel, Errors[MyError, OtherError]]:
    if how_iam == "me":
        return ResponseModel(message="hello")
    if how_iam.isdigit():
        raise OtherError
    raise MyError("I don't know you")

Keep responses data model

To automatically catch errors from endpoints and include the status code specified in them in the response, you need to add the error handler ResponseError.handler to the FastAPI application.

app.add_exception_handler(ResponseError, ResponseError.handler)

Handle MyError Handle OtherError

Project details


Download files

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

Source Distribution

annotated_fastapi_router-0.1.0.tar.gz (10.9 kB view details)

Uploaded Source

Built Distribution

File details

Details for the file annotated_fastapi_router-0.1.0.tar.gz.

File metadata

  • Download URL: annotated_fastapi_router-0.1.0.tar.gz
  • Upload date:
  • Size: 10.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.1 Linux/6.5.0-1022-azure

File hashes

Hashes for annotated_fastapi_router-0.1.0.tar.gz
Algorithm Hash digest
SHA256 cb2c37cbe7e9467ff1c2fb2984f387573e8b5a99efa356584187943db4e2b130
MD5 4aeff5a360910add66fab75bef2b717f
BLAKE2b-256 3db1eb50222fb44cf202dbf67dea2ecf8376ee513c9230b11d505c56153b032a

See more details on using hashes here.

File details

Details for the file annotated_fastapi_router-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for annotated_fastapi_router-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8cca229a4ae1c5a64f3f06c2e1e24618916f74c045d0ab6f7c095be7cff6e2a1
MD5 022b5dd62dd0635bbaa72d86de47d663
BLAKE2b-256 18fefa366676b1e9b530aeb796797542258ec2579361a32806aaeb35f61dee1e

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page