Type-safe, extensible, strictly validated API response system for Python apps
Project description
Typed API Response
A unified, type-safe API response format for Python — with full type inference, Pydantic support, and consistent structure for both success and error responses. Just pass your data or exception — build_api_response() handles the rest.
Works seamlessly with FastAPI, Django Ninja, or any Pydantic-based Python project. Accepts Pydantic models, dataclasses, or any structured object.
🧪 Type Safety
This library is:
- Designed for Pylance and mypy strict mode
- Fully generic — type-safe through all layers
- Uses overloads to preserve type inference
No need to type hint manually:
response = build_api_response(data=MySchema(...), status=200)
# response.payload.data is inferred as MySchema ✅
➡️ Want proof? See the typecheck file
This is a static analysis file for
mypy. It usesreveal_type()to confirm that generic types and payload structures are preserved correctly.
You can run it withmypyor open it in VSCode and hover to inspect types inline — no need to execute the file.
🔧 Features
- ✅ Typed response builders for both success and error responses
- ✅ Fully generic, Pylance-compliant with strict mode enabled
- ✅ Single unified function:
build_api_response(...) - ✅ Extensible metadata support via
ResponseMeta - ✅ Automatically distinguishes between
dataanderror - ✅ Raises clean custom exceptions on misconfiguration
🚀 Getting Started
Install with pip
pip install typed-api-response
Define your Pydantic response schema
from pydantic import BaseModel
class MyOutputSchema(BaseModel):
product_name: str
description: str
price: float
qty: int
on_sale: bool
Create a typed API response:
@router.post("/foo")
def foo():
your_data = MyOutputSchema(
product_name="Big Bag of Rice",
description="The world's greatest rice ever made. Anywhere. Ever.",
price=17.99,
qty=47328,
on_sale=False,
)
response = build_api_response(data=your_data, status=200)
# response.payload.data is inferred as MyOutputSchema ✅
✅ build_api_response() accepts any Pydantic model or well-typed object (e.g. a dataclass) and wraps it into a fully structured, metadata-rich response — with full type hint propagation and IDE support via generics.
Handling errors just as cleanly
You can also return exceptions using the same unified response format:
try:
...
except Exception as e:
return build_api_response(error=e, status=418)
✅ build_api_response() wraps the exception in a type-safe, structured error payload — so your failure responses stay as consistent and predictable as your success ones.
🧱 API Structure
Unified Interface
def build_api_response(
*,
data: T | None = None,
error: Exception | None = None,
status: int,
meta: ResponseMeta | None = None,
) -> ApiSuccessResponse[T] | ApiErrorResponse
- Provide either
dataorerror, not both metalets you attach timing, versioning, request ID, etc.- If neither
datanorerroris passed, raisesApiResponseBuilderError
Success Response Format
{
"status": 200,
"meta": {
"duration": null,
"extra": null,
"method": null,
"path": null,
"request_id": null,
"timestamp": "2025-07-30T04:33:44.833Z",
"version": null
},
"payload": {
"success": true,
"data": {
"product_name": "Big Bag of Rice",
"description": "The world's greatest rice ever made. Anywhere. Ever.",
"price": 17.99,
"qty": 47328,
"on_sale": false
},
"error": null
}
}
Error Response Format
{
"status": 418,
"meta": {
"duration": null,
"extra": null,
"method": null,
"path": null,
"request_id": null,
"timestamp": "2025-07-30T00:13:55.531Z",
"version": null
},
"payload": {
"success": false,
"data": null,
"error": {
"type": "ZeroDivisionError",
"msg": "division by zero"
}
}
}
🧠 Customizing Metadata
Use ResponseMeta to attach custom fields:
meta = ResponseMeta(
request_id="abc123",
version="v1.2.0",
extra={"model": "en_streetninja", "debug": True}
)
return build_api_response(data=result, status=200, meta=meta)
🛡️ Exceptions
This package raises:
ApiResponseBuilderError– if payload generation failsApiPayloadBuilderError– if payload data is inconsistent or incomplete
📦 Components
ApiResponseBuilder– abstract base class for buildersApiSuccessResponseBuilder– buildsApiSuccessResponse[T]ApiErrorResponseBuilder– buildsApiErrorResponseResponseMeta– optional metadata blockPayload/SuccessPayload[T]/ErrorPayload– structured payload schemas
☕ Support This Project
If this saved you time or made your API cleaner, feel free to buy me a coffee. Thanks for your support 🙏
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file typed_api_response-1.0.0.tar.gz.
File metadata
- Download URL: typed_api_response-1.0.0.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f4a1217cc1dd8fa3be925e903f63e84c7b561e3f47828bc0acc364fbc752176
|
|
| MD5 |
21a526997e2881775b5517717de1806c
|
|
| BLAKE2b-256 |
ec47d90b72f3ca1519a3c2448e8fd273b4283969ec1e78f5aa7bdb72adad51f9
|
File details
Details for the file typed_api_response-1.0.0-py3-none-any.whl.
File metadata
- Download URL: typed_api_response-1.0.0-py3-none-any.whl
- Upload date:
- Size: 10.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c751f90f2c8dbf11f07df1c96c28facb08f627874cc39aa3da3b88ea41c830bd
|
|
| MD5 |
9c34508128cda56fead3870abd663ca9
|
|
| BLAKE2b-256 |
ca52db2b1101aea385de2dbee13678d07069aacfdba9ce10dae5f9defe92f187
|