Skip to main content

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 uses reveal_type() to confirm that generic types and payload structures are preserved correctly.
You can run it with mypy or 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 data and error
  • ✅ 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 data or error, not both
  • meta lets you attach timing, versioning, request ID, etc.
  • If neither data nor error is passed, raises ApiResponseBuilderError

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 fails
  • ApiPayloadBuilderError – if payload data is inconsistent or incomplete

📦 Components

  • ApiResponseBuilder – abstract base class for builders
  • ApiSuccessResponseBuilder – builds ApiSuccessResponse[T]
  • ApiErrorResponseBuilder – builds ApiErrorResponse
  • ResponseMeta – optional metadata block
  • Payload / 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typed_api_response-1.0.0.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

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

typed_api_response-1.0.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

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

Hashes for typed_api_response-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1f4a1217cc1dd8fa3be925e903f63e84c7b561e3f47828bc0acc364fbc752176
MD5 21a526997e2881775b5517717de1806c
BLAKE2b-256 ec47d90b72f3ca1519a3c2448e8fd273b4283969ec1e78f5aa7bdb72adad51f9

See more details on using hashes here.

File details

Details for the file typed_api_response-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for typed_api_response-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c751f90f2c8dbf11f07df1c96c28facb08f627874cc39aa3da3b88ea41c830bd
MD5 9c34508128cda56fead3870abd663ca9
BLAKE2b-256 ca52db2b1101aea385de2dbee13678d07069aacfdba9ce10dae5f9defe92f187

See more details on using hashes here.

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