Skip to content

Response Formatting

Standardized API responses for consistent client integration.

Response Helpers

Import from app/utils/responses.py:

python
from app.utils.responses import (
    success_response,
    error_response,
    paginated_response
)

success_response

Return successful operation results.

python
@router.get("/{id}")
async def get_user(id: int, session: AsyncSession = Depends(get_session)):
    user = await UserController.get_by_id(session, id)
    return success_response(data=user)

Output

json
{
  "success": true,
  "data": {
    "id": 1,
    "email": "user@example.com",
    "name": "John Doe"
  }
}

With Message

python
return success_response(
    data=user,
    message="User retrieved successfully"
)
json
{
  "success": true,
  "data": {...},
  "message": "User retrieved successfully"
}

List Data

python
users = await UserController.get_all(session)
return success_response(data=users)
json
{
  "success": true,
  "data": [
    {"id": 1, "name": "John"},
    {"id": 2, "name": "Jane"}
  ]
}

error_response

Return error information.

python
return error_response(
    message="User not found",
    code="NOT_FOUND",
    status_code=404
)

Output

json
{
  "success": false,
  "error": {
    "message": "User not found",
    "code": "NOT_FOUND"
  }
}

With Details

python
return error_response(
    message="Validation failed",
    code="VALIDATION_ERROR",
    status_code=422,
    details={
        "email": ["Invalid email format"],
        "password": ["Must be at least 8 characters"]
    }
)
json
{
  "success": false,
  "error": {
    "message": "Validation failed",
    "code": "VALIDATION_ERROR",
    "details": {
      "email": ["Invalid email format"],
      "password": ["Must be at least 8 characters"]
    }
  }
}

paginated_response

Return paginated list data.

python
from app.utils.pagination import paginate, PaginationParams

@router.get("/")
async def list_users(
    page: int = 1,
    per_page: int = 20,
    session: AsyncSession = Depends(get_session)
):
    params = PaginationParams(page=page, per_page=per_page)
    result = await paginate(session, select(User), params)

    return paginated_response(
        items=result.items,
        page=result.page,
        per_page=result.per_page,
        total=result.total
    )

Output

json
{
  "success": true,
  "data": [
    {"id": 1, "name": "John"},
    {"id": 2, "name": "Jane"}
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 45,
    "pages": 3,
    "has_next": true,
    "has_prev": false
  }
}

Response Schemas

For API documentation, define response schemas:

python
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional, List

T = TypeVar('T')

class SuccessResponse(BaseModel, Generic[T]):
    success: bool = True
    data: T
    message: Optional[str] = None

class ErrorDetail(BaseModel):
    message: str
    code: str
    details: Optional[dict] = None

class ErrorResponse(BaseModel):
    success: bool = False
    error: ErrorDetail

class PaginationMeta(BaseModel):
    page: int
    per_page: int
    total: int
    pages: int
    has_next: bool
    has_prev: bool

class PaginatedResponse(BaseModel, Generic[T]):
    success: bool = True
    data: List[T]
    meta: PaginationMeta

Using in Routes

python
@router.get("/", response_model=PaginatedResponse[UserResponse])
async def list_users():
    ...

@router.get("/{id}", response_model=SuccessResponse[UserResponse])
async def get_user(id: int):
    ...

HTTP Status Codes

CodeUsage
200Successful GET, PUT
201Successful POST (created)
204Successful DELETE (no content)
400Bad request
401Unauthorized
403Forbidden
404Not found
409Conflict
422Validation error
429Rate limited
500Server error

Best Practices

  1. Always use helpers - Don't construct responses manually
  2. Include codes - Error codes help clients handle errors programmatically
  3. Be consistent - All endpoints should return the same structure
  4. Use appropriate status codes - Match HTTP semantics
  5. Don't expose internals - Hide stack traces in production

Released under the MIT License.