Skip to content

Schemas

Standardized Pydantic response models for consistent API responses across your FastAPI application.

Overview

The schemas module provides generic response wrappers that enforce a uniform response structure. All models use from_attributes=True for ORM compatibility and validate_assignment=True for runtime type safety.

Response models

Response[T]

The most common wrapper for a single resource response.

from fastapi_toolsets.schemas import Response

@router.get("/users/{id}")
async def get_user(user: User = UserDep) -> Response[UserSchema]:
    return Response(data=user, message="User retrieved")

Paginated response models

Three classes wrap paginated list results. Pick the one that matches your endpoint's strategy:

Class pagination type pagination_type field Use when
OffsetPaginatedResponse[T] OffsetPagination "offset" (fixed) endpoint always uses offset
CursorPaginatedResponse[T] CursorPagination "cursor" (fixed) endpoint always uses cursor
PaginatedResponse[T] OffsetPagination \| CursorPagination unified endpoint supporting both strategies

OffsetPaginatedResponse[T]

Added in v2.3.0

Use as the return type when the endpoint always uses offset_paginate. The pagination field is guaranteed to be an OffsetPagination object; the response always includes a pagination_type: "offset" discriminator.

from fastapi_toolsets.schemas import OffsetPaginatedResponse

@router.get("/users")
async def list_users(
    page: int = 1,
    items_per_page: int = 20,
) -> OffsetPaginatedResponse[UserSchema]:
    return await UserCrud.offset_paginate(
        session, page=page, items_per_page=items_per_page, schema=UserSchema
    )

Response shape:

{
  "status": "SUCCESS",
  "pagination_type": "offset",
  "data": ["..."],
  "pagination": {
    "total_count": 100,
    "page": 1,
    "items_per_page": 20,
    "has_more": true
  }
}

CursorPaginatedResponse[T]

Added in v2.3.0

Use as the return type when the endpoint always uses cursor_paginate. The pagination field is guaranteed to be a CursorPagination object; the response always includes a pagination_type: "cursor" discriminator.

from fastapi_toolsets.schemas import CursorPaginatedResponse

@router.get("/events")
async def list_events(
    cursor: str | None = None,
    items_per_page: int = 20,
) -> CursorPaginatedResponse[EventSchema]:
    return await EventCrud.cursor_paginate(
        session, cursor=cursor, items_per_page=items_per_page, schema=EventSchema
    )

Response shape:

{
  "status": "SUCCESS",
  "pagination_type": "cursor",
  "data": ["..."],
  "pagination": {
    "next_cursor": "eyJpZCI6IDQyfQ==",
    "prev_cursor": null,
    "items_per_page": 20,
    "has_more": true
  }
}

PaginatedResponse[T]

Return type for endpoints that support both pagination strategies via a pagination_type query parameter (using paginate()).

When used as a return annotation, PaginatedResponse[T] automatically expands to Annotated[Union[CursorPaginatedResponse[T], OffsetPaginatedResponse[T]], Field(discriminator="pagination_type")], so FastAPI emits a proper oneOf + discriminator in the OpenAPI schema with no extra boilerplate:

from fastapi_toolsets.crud import PaginationType
from fastapi_toolsets.schemas import PaginatedResponse

@router.get("/users")
async def list_users(
    pagination_type: PaginationType = PaginationType.OFFSET,
    page: int = 1,
    cursor: str | None = None,
    items_per_page: int = 20,
) -> PaginatedResponse[UserSchema]:
    return await UserCrud.paginate(
        session,
        pagination_type=pagination_type,
        page=page,
        cursor=cursor,
        items_per_page=items_per_page,
        schema=UserSchema,
    )

Pagination metadata models

The optional filter_attributes field is populated when facet_fields are configured on the CRUD class (see Filter attributes). It is None by default and can be hidden from API responses with response_model_exclude_none=True.

ErrorResponse

Returned automatically by the exceptions handler.


API Reference