Skip to content

API Layer Contract

Responsibilities

API layer owns: - request/response schema validation - HTTP status mapping - dependency resolution via Depends

API layer must not own business decisions.

Example

@router.get("/{user_id}", response_model=UserRead)
def get_user(user_id: int, service: UserService = Depends(get_user_service)):
    user = service.get_user(user_id)
    return UserRead.model_validate(user)

DO

  • Keep handlers thin.
  • Convert domain result to API schema.
  • Use explicit status codes on write endpoints.

DON'T

  • Execute SQLAlchemy in route handlers.
  • Embed business branching (if role == ...) in handlers.
  • Build ad-hoc JSON response contracts per route.

Common Mistakes

  • Catching all exceptions in route and returning 200 with error body.
  • Returning ORM objects directly.

When to Break the Rule

Never for business endpoints. For very simple liveness checks, route can call a minimal service wrapper.