Skip to content

Instantly share code, notes, and snippets.

@nkhitrov
Created August 24, 2022 20:31
Show Gist options
  • Save nkhitrov/e430850eb440665474eef17fc3062ead to your computer and use it in GitHub Desktop.
Save nkhitrov/e430850eb440665474eef17fc3062ead to your computer and use it in GitHub Desktop.
FastAPI generic request, response schemas
from typing import Any, Generic, Type, TypeAlias, TypeVar
from fastapi import status as http_status_code
from pydantic import Field
from pydantic.generics import GenericModel
_Model = TypeVar("_Model")
GenericResponse: TypeAlias = GenericModel
class BaseError(BaseModel):
"""Error response as defined in
https://datatracker.ietf.org/doc/html/rfc7807.
One difference is that the member "type" is not a URI
"""
type: Optional[str] = Field(title="The name of the class of the error")
title: Optional[str] = Field(
title="A short, human-readable summary of the problem that does not change from occurence to occurence",
)
detail: Optional[str] = Field(title="А human-readable explanation specific to this occurrence of the problem")
instance: Optional[Any] = Field(title="Identifier for this specific occurrence of the problem")
class BaseResponse(BaseModel):
status: int = Field(..., title="Status code of request.", example=status_code.HTTP_200_OK)
error: BaseError | None = Field(None, title="Errors data")
payload: Any | None = Field(None, title="Payload data.")
class NoContentResponse(BaseResponse):
status: int = status_code.HTTP_204_NO_CONTENT
class DataResponse(GenericResponse, Generic[_Model]):
data: _Model | list[_Model]
class OkResponse(GenericResponse, Generic[_Model]):
"""
# serializers.py
>>> class UserID(BaseModel):
>>> user_id: int = Field(..., example=12345)
# controllers.py
>>> @router.get("/foo", response_model=Response[UserID], status_code=200)
>>> async def foo_func():
>>> user_id = 12345
>>> return OkResponse.new(
>>> status_code=200, model=UserID, data=UserID(user_id=user_id),
>>> )
"""
status: int = Field(..., title="Status code of request.", example=http_status_code.HTTP_200_OK)
error: BaseError | None = Field(None title="Errors")
payload: DataResponse[_Model] = Field(title="Payload data.")
@classmethod
def new(
cls,
*,
status_code: int,
model: Type[_Model] | list[Type[_Model]] | Any,
data: _Model | Any,
) -> "OkResponse[_Model]":
return cls[model](status=status_code, payload=DataResponse[model](data=data)) # type: ignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment