Skip to main content

Stop duplicating your Pydantic schemas. Map FastAPI request parameters into a single DTO.

Project description

🎩 FastAPI Magic DTO

Python FastAPI Pydantic License: MIT

Stop duplicating your Pydantic schemas. Keep your Application layer pure.

fastapi-magic-dto is a lightweight utility that allows you to map FastAPI request parameters (Path, Query, Header, Cookie, and Body) directly into a single, clean DTO (Data Transfer Object) without polluting your business logic with HTTP-specific dependencies.

🤬 The Problem

When building layered architectures (Clean Architecture, CQRS, Hexagonal), your Application layer (Interactors/Use Cases) should know nothing about HTTP.

Normally, to extract data from multiple HTTP locations (like a User ID from the URL Path, and the User Name from the JSON Body), FastAPI forces you to either:

  1. Write two separate schemas (one for FastAPI, one for the Application layer) and map them manually.
  2. Pollute your Application DTO with fastapi.Path and fastapi.Query.

❌ Bad (Leaking HTTP into Domain)

from fastapi import Path, Body
from pydantic import BaseModel

class UpdateUserDTO(BaseModel):
    user_id: int = Path(...)  # 🔴 Domain now depends on FastAPI!
    name: str = Body(...)

❌ Another Bad Approach (Duplication & Manual Repacking)

To keep the Domain DTO clean, you are forced to create a separate "Body" schema just for FastAPI, and then manually unpack and repack the data in every single endpoint. This leads to massive code duplication and boilerplate.

from fastapi import Path, Body
from pydantic import BaseModel

# 🔴 1. Fake schema just to please FastAPI
class UpdateUserBody(BaseModel):
    name: str

# 2. Pure Application DTO
class UpdateUserDTO(BaseModel):
    user_id: int
    name: str

@app.put("/users/{user_id}")
async def update_user(
    user_id: int = Path(...),
    body: UpdateUserBody = Body(...)
):
    # 🔴 3. Ugly manual repacking everywhere!
    data = UpdateUserDTO(user_id=user_id, **body.model_dump())
    
    # Now pass `data` to your interactor...
    return await interactor(data)

✨ The Solution

fastapi-magic-dto allows you to write a pure Pydantic DTO (or standard Dataclass), and use an elegant type hint at the Router level to tell FastAPI exactly where to find the data.

✅ Good (Pure Domain + MagicDTO)

from pydantic import BaseModel
from fastapi_magic_dto import MagicDTO, P

# 1. Pure Application DTO. Zero HTTP logic.
class UpdateUserDTO(BaseModel):
    user_id: int
    name: str

# 2. FastAPI Router mapping HTTP to the DTO
@app.put("/users/{user_id}")
async def update_user(
    data: MagicDTO[UpdateUserDTO, P.user_id]
):
    # `user_id` is extracted from the URL Path.
    # `name` is automatically extracted from the JSON Body.
    return data

📦 Installation

pip install fastapi-magic-dto

🚀 Features

  • Zero Duplication: Write one DTO for both validation and business logic.
  • Perfect OpenAPI (Swagger): Fully compatible with FastAPI's automatic schema generation. Path, Query, and Body parameters appear exactly where they should.
  • Type Checker Friendly: Mypy, Pylance, and Pyright will see the correct types. Autocomplete works perfectly.
  • Dataclass Support: Works natively with standard Python @dataclass—no Pydantic required!
  • Everything Supported: Extract data from Path (P), Query (Q), Header (H), Cookie (C), and JSON Body.

🛠 Usage & Markers

The library provides 4 intuitive markers to define the origin of your fields:

Marker HTTP Location Example
P Path P.item_id maps to /items/{item_id}
Q Query Q.limit maps to ?limit=10
H Header H.user_agent maps to User-Agent: ...
C Cookie C.session maps to Cookie: session=...

Note: Any field in your DTO that is not explicitly marked will automatically be extracted from the JSON Request Body.

📖 Complete Example

You can extract data from everywhere simultaneously into a single DTO.

class CreateItemDTO(BaseModel):
    category_id: int
    name: str = Field(..., min_length=2, max_length=100)
    price: float = Field(...)


class CreateItemInteractor:
    async def __call__(self, data: CreateItemDTO) -> dict:
        # Business logic goes here (e.g., saving to the database)
        return {
            "status": "success",
            "message": f"Item '{data.name}' created in category {data.category_id}.",
            "item_data": data.model_dump(),
        }


create_item_interactor = CreateItemInteractor()

app = FastAPI()


@app.post("/items/{category_id}", tags=["Items"])
async def create_item(
    # MagicDTO extracts `category_id` from Path, and `name`/`price` from Body
    data: MagicDTO[CreateItemDTO, P.category_id],
) -> dict:
    return await create_item_interactor(data)

🐍 Using Standard Dataclasses

Don't want to use Pydantic? No problem. MagicDTO fully supports standard Python dataclasses.

from dataclasses import dataclass
from fastapi_magic_dto import MagicDTO, P, Q

@dataclass
class SearchDTO:
    category_id: int
    limit: int = 10
    search_term: str = ""

@app.get("/search/{category_id}")
async def search_items(
    data: MagicDTO[SearchDTO, P.category_id, Q.limit, Q.search_term]
):
    return data

📝 License

MIT License. See LICENSE for details.

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

fastapi_magic_dto-0.1.1.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_magic_dto-0.1.1-py3-none-any.whl (6.8 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_magic_dto-0.1.1.tar.gz.

File metadata

  • Download URL: fastapi_magic_dto-0.1.1.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fastapi_magic_dto-0.1.1.tar.gz
Algorithm Hash digest
SHA256 72f62d008e5afa7b5de4d7cc50da97bcacdf0f344537f6b5dfcf53261d202978
MD5 6c39ace9f8e16e6bcffba145af58fb71
BLAKE2b-256 65ec5451da4bd02fa731797e28161917d519e06e6318153acd91c05c08666a3c

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_magic_dto-0.1.1.tar.gz:

Publisher: publish.yml on toxazhl/fastapi_magic_dto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastapi_magic_dto-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_magic_dto-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ad67b040a278b6fd4331de30a3800f3c284488f52c21128204aedaaee62593a1
MD5 baa608d2d1916e703dd6e29ee562d376
BLAKE2b-256 31ab3a437afe59a216e642dced506f74fdac827aff5e79711c8f0c80975558a8

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_magic_dto-0.1.1-py3-none-any.whl:

Publisher: publish.yml on toxazhl/fastapi_magic_dto

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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