
Response Models and Consistent Formats
Master the art of the response. Learn how to design standardized response wrappers and use Pydantic to ensure every endpoint speaks the same language.
Response Models and Consistent Formats
We’ve already seen how response_model filters data. But in a large production system, you want more than just filtering. You want a Consistent Response Wrapper.
Frontend developers shouldn't have to guess whether your API returns a raw list, a dictionary with a "data" key, or an object with "results" and "count".
In this lesson, we learn how to design standardized, predictable responses using Pydantic.
1. The "Envelope" Pattern
The Envelope Pattern involves wrapping your actual data inside a consistent structure. This is perfect for pagination or metadata.
from typing import Generic, TypeVar, List
from pydantic import BaseModel
# A Generic type for our data
T = TypeVar("T")
class ResponseEnvelope(BaseModel, Generic[T]):
status: str = "success"
data: T
message: str | None = None
# Using it in an endpoint
@app.get("/users", response_model=ResponseEnvelope[List[UserOut]])
def get_users():
users = db.get_all()
return {"data": users}
2. Standardized Pagination
If your API handles lists, you should include pagination metadata in every response.
class PaginationMeta(BaseModel):
total_count: int
page: int
page_size: int
class PaginatedResponse(BaseModel, Generic[T]):
items: List[T]
meta: PaginationMeta
@app.get("/items", response_model=PaginatedResponse[Item])
def list_items(page: int = 1):
items, total = db.get_paginated(page)
return {
"items": items,
"meta": {
"total_count": total,
"page": page,
"page_size": 10
}
}
3. Success vs. Error Consistency
Ideally, your success responses and your error responses should share some common DNA (like a status field), making them easier to handle in a global Frontend interceptor (like an Axios interceptor).
4. Why Consistency Matters for AI Agents
Wait, this isn't just for humans! When an AI Agent calls your API (using Tool Calling), it expects a predictable structure. If the structure is erratic, the agent might fail to parse the "JSON" correctly, leading to reasoning errors.
- Structured output = Reliable agents.
Visualizing a Standardized API
graph TD
A["API Request"] --> B{"FastAPI Handler"}
B -- "Success" --> C["Success Wrapper: {status: 'success', data: {...}}"]
B -- "Error" --> D["Error Wrapper: {status: 'error', detail: '...'}"]
C --> E["Client: Predictable Parsing"]
D --> E
Summary
- Response Wrapping: Use Envelopes to add metadata.
- Generics: Use
Generic[T]to create reusable response models for any resource. - Pagination: Always include
metawhen returning lists. - Reliability: Consistency makes your API easier to use for both Frontend developers and AI Agents.
In the next lesson, we wrap up Module 7 with Exercises on error handling and response design.
Exercise: The Envelope Designer
Design a Generic response model called ActionResponse[T].
It should have:
action_id(A UUID string).result(The generic typeT).timestamp(ISO date string).
Show how you would use this for a POST /calculate-sum endpoint that returns an integer.