DTO Mapping

dequest supports automatic response mapping into custom DTO classes using the dto_class parameter. it’s supported by all client decorators: @sync_client, @async_client, and @async_await_client.

When consume=ConsumerType.JSON, the JSON response is deserialized and mapped into the provided DTO class. When consume=ConsumerType.XML, the XML response is parsed and mapped in the same way.

Assuming we have the following JSON response from the API:

{
    "status": "ok",
    "data": {
        "item": {
            "id": 42,
            "name": "Widget",
        }
    }
}
from dataclasses import dataclass

@dataclass
class ItemDTO:
    id: int
    name: str

@sync_client(
    url="https://api.example.com/items/{item_id}",
    dto_class=ItemDTO,
    consume=ConsumerType.JSON, # Optional since JSON is the default
    source_field="data.item",
)
def get_item(item_id: int):
    pass

Only the selected source_field is mapped into ItemDTO.

Nested DTOs are supported automatically:

from dataclasses import dataclass

@dataclass
class ProfileDTO:
    id: int
    email: str

@dataclass
class UserDTO:
    id: int
    name: str
    profile: ProfileDTO


@async_await_client(
    url="https://api.example.com/users/{user_id}",
    dto_class=UserDTO,
    source_field="data.user",
)
async def get_user(user_id: int):
    pass

dequest will create a ProfileDTO object inside the mapped UserDTO.

Lists of DTOs are also supported when the selected source field resolves to an array:

{
    "status": "ok",
    "data": {
        "items": [
            {"id": 1, "name": "Item 1"},
            {"id": 2, "name": "Item 2"}
        ]
    }
}

No need to change the DTO definition:

@async_client(
    url="https://api.example.com/items",
    dto_class=ItemDTO,
    consume=ConsumerType.JSON,
    source_field="data.items",
)
def list_items():
    pass

The list_items function returns a list of ItemDTO objects.

Important notes:

  • dto_class cannot be used with ConsumerType.TEXT.

  • source_field is optional; if omitted, dequest attempts to map the entire response into the DTO.

  • This mapping behavior is identical across @sync_client, @async_client, and @async_await_client.