Skip to content

Response Wrappers

Wrappers define the outer shape of API responses. They are optional, but they make client code easier when every endpoint returns a predictable envelope.

Built-In Wrappers

Wrapper Use For Shape
ResponseDataWrapper single object responses {"data": {...}}
ListDataWrapper non-paginated list responses {"data": [...]}
PaginatedResponseDataWrapper paginated list responses {"data": [...], "meta": {...}}

Single Object Wrapper

from fastapi_ronin.wrappers import ResponseDataWrapper


@viewset(router)
class CompanyViewSet(ModelViewSet[Company]):
    model = Company
    read_schema = CompanySchema
    create_schema = CompanyCreateSchema

    single_wrapper = ResponseDataWrapper
{
  "data": {
    "id": 1,
    "name": "Acme"
  }
}

single_wrapper is used for retrieve, create, and update responses.

Paginated List Wrapper

from fastapi_ronin.pagination import PageNumberPagination
from fastapi_ronin.wrappers import PaginatedResponseDataWrapper


@viewset(router)
class CompanyViewSet(ModelViewSet[Company]):
    pagination = PageNumberPagination
    list_wrapper = PaginatedResponseDataWrapper
{
  "data": [
    {"id": 1, "name": "Acme"}
  ],
  "meta": {
    "page": 1,
    "size": 10,
    "total": 1,
    "pages": 1
  }
}

Plain List Wrapper

Use ListDataWrapper when pagination is disabled but you still want a data envelope.

from fastapi_ronin.pagination import DisabledPagination
from fastapi_ronin.wrappers import ListDataWrapper


@viewset(router)
class CompanyViewSet(ModelViewSet[Company]):
    pagination = DisabledPagination
    list_wrapper = ListDataWrapper

Custom Single Wrapper

from datetime import datetime

from fastapi_ronin.types import T
from fastapi_ronin.wrappers import ResponseWrapper


class ApiResponse(ResponseWrapper[T]):
    data: T
    success: bool
    timestamp: str

    @classmethod
    def wrap(cls, data: T, **kwargs) -> 'ApiResponse':
        return cls(data=data, success=True, timestamp=datetime.now().isoformat())

Custom Paginated Wrapper

from fastapi_ronin.pagination import PageNumberPagination
from fastapi_ronin.types import T
from fastapi_ronin.wrappers import PaginatedResponseWrapper


class ApiPage(PaginatedResponseWrapper[T, PageNumberPagination]):
    items: list[T]
    total: int
    page: int

    @classmethod
    def wrap(cls, data: list[T], pagination: PageNumberPagination, **kwargs) -> 'ApiPage':
        return cls(items=data, total=pagination.total, page=pagination.page)

Guidance

  • Use wrappers from the start if API consistency matters.
  • Pair PaginatedResponseDataWrapper with a real pagination class.
  • Use ListDataWrapper only for non-paginated lists.
  • Keep wrapper fields stable; clients will depend on them.