Python API Design Best Practices¶
Objective: Master senior-level Python API design patterns for production systems. When you need to build robust, scalable APIs, when you want to implement comprehensive API versioning, when you need enterprise-grade API design strategiesโthese best practices become your weapon of choice.
Core Principles¶
- RESTful Design: Follow REST principles for consistent API design
- Versioning: Implement proper API versioning strategies
- Documentation: Provide comprehensive API documentation
- Error Handling: Implement consistent error responses
- Security: Build secure APIs with proper authentication and authorization
RESTful API Design¶
Resource-Based Design¶
# python/01-restful-api-design.py
"""
RESTful API design patterns and resource-based architecture
"""
from typing import List, Dict, Any, Optional, Union
from dataclasses import dataclass, asdict
from enum import Enum
import json
from datetime import datetime
from abc import ABC, abstractmethod
class HTTPMethod(Enum):
"""HTTP methods"""
GET = "GET"
POST = "POST"
PUT = "PUT"
PATCH = "PATCH"
DELETE = "DELETE"
class HTTPStatus(Enum):
"""HTTP status codes"""
OK = 200
CREATED = 201
NO_CONTENT = 204
BAD_REQUEST = 400
UNAUTHORIZED = 401
FORBIDDEN = 403
NOT_FOUND = 404
METHOD_NOT_ALLOWED = 405
CONFLICT = 409
UNPROCESSABLE_ENTITY = 422
INTERNAL_SERVER_ERROR = 500
@dataclass
class APIResponse:
"""Standard API response"""
status_code: int
data: Optional[Any] = None
message: Optional[str] = None
errors: Optional[List[str]] = None
metadata: Optional[Dict[str, Any]] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
response = {
"status_code": self.status_code,
"success": 200 <= self.status_code < 300
}
if self.data is not None:
response["data"] = self.data
if self.message:
response["message"] = self.message
if self.errors:
response["errors"] = self.errors
if self.metadata:
response["metadata"] = self.metadata
return response
@dataclass
class PaginationInfo:
"""Pagination information"""
page: int
per_page: int
total: int
total_pages: int
has_next: bool
has_prev: bool
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
return {
"page": self.page,
"per_page": self.per_page,
"total": self.total,
"total_pages": self.total_pages,
"has_next": self.has_next,
"has_prev": self.has_prev
}
class Resource(ABC):
"""Base resource class"""
def __init__(self, resource_id: str, created_at: datetime = None, updated_at: datetime = None):
self.id = resource_id
self.created_at = created_at or datetime.utcnow()
self.updated_at = updated_at or datetime.utcnow()
@abstractmethod
def to_dict(self) -> Dict[str, Any]:
"""Convert resource to dictionary"""
pass
@classmethod
@abstractmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Resource':
"""Create resource from dictionary"""
pass
@dataclass
class User(Resource):
"""User resource"""
name: str
email: str
is_active: bool = True
def to_dict(self) -> Dict[str, Any]:
"""Convert user to dictionary"""
return {
"id": self.id,
"name": self.name,
"email": self.email,
"is_active": self.is_active,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat()
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'User':
"""Create user from dictionary"""
return cls(
resource_id=data["id"],
name=data["name"],
email=data["email"],
is_active=data.get("is_active", True),
created_at=datetime.fromisoformat(data["created_at"]),
updated_at=datetime.fromisoformat(data["updated_at"])
)
class APIResource:
"""API resource handler"""
def __init__(self, resource_type: str):
self.resource_type = resource_type
self.resources: Dict[str, Resource] = {}
self.next_id = 1
def create(self, data: Dict[str, Any]) -> APIResponse:
"""Create resource"""
try:
# Validate required fields
if not data.get("name"):
return APIResponse(
status_code=HTTPStatus.BAD_REQUEST.value,
errors=["Name is required"]
)
# Create resource
resource_id = str(self.next_id)
self.next_id += 1
if self.resource_type == "user":
resource = User(
resource_id=resource_id,
name=data["name"],
email=data["email"],
is_active=data.get("is_active", True)
)
else:
raise ValueError(f"Unknown resource type: {self.resource_type}")
self.resources[resource_id] = resource
return APIResponse(
status_code=HTTPStatus.CREATED.value,
data=resource.to_dict(),
message=f"{self.resource_type.title()} created successfully"
)
except Exception as e:
return APIResponse(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR.value,
errors=[str(e)]
)
def get(self, resource_id: str) -> APIResponse:
"""Get resource by ID"""
if resource_id not in self.resources:
return APIResponse(
status_code=HTTPStatus.NOT_FOUND.value,
errors=[f"{self.resource_type.title()} not found"]
)
resource = self.resources[resource_id]
return APIResponse(
status_code=HTTPStatus.OK.value,
data=resource.to_dict()
)
def list(self, page: int = 1, per_page: int = 10) -> APIResponse:
"""List resources with pagination"""
total = len(self.resources)
total_pages = (total + per_page - 1) // per_page
start = (page - 1) * per_page
end = start + per_page
resources = list(self.resources.values())[start:end]
data = [resource.to_dict() for resource in resources]
pagination = PaginationInfo(
page=page,
per_page=per_page,
total=total,
total_pages=total_pages,
has_next=page < total_pages,
has_prev=page > 1
)
return APIResponse(
status_code=HTTPStatus.OK.value,
data=data,
metadata={"pagination": pagination.to_dict()}
)
def update(self, resource_id: str, data: Dict[str, Any]) -> APIResponse:
"""Update resource"""
if resource_id not in self.resources:
return APIResponse(
status_code=HTTPStatus.NOT_FOUND.value,
errors=[f"{self.resource_type.title()} not found"]
)
try:
resource = self.resources[resource_id]
# Update fields
if "name" in data:
resource.name = data["name"]
if "email" in data:
resource.email = data["email"]
if "is_active" in data:
resource.is_active = data["is_active"]
resource.updated_at = datetime.utcnow()
return APIResponse(
status_code=HTTPStatus.OK.value,
data=resource.to_dict(),
message=f"{self.resource_type.title()} updated successfully"
)
except Exception as e:
return APIResponse(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR.value,
errors=[str(e)]
)
def delete(self, resource_id: str) -> APIResponse:
"""Delete resource"""
if resource_id not in self.resources:
return APIResponse(
status_code=HTTPStatus.NOT_FOUND.value,
errors=[f"{self.resource_type.title()} not found"]
)
del self.resources[resource_id]
return APIResponse(
status_code=HTTPStatus.NO_CONTENT.value,
message=f"{self.resource_type.title()} deleted successfully"
)
# Usage examples
def example_restful_api():
"""Example RESTful API usage"""
# Create API resource
user_api = APIResource("user")
# Create user
create_data = {
"name": "John Doe",
"email": "john@example.com",
"is_active": True
}
create_response = user_api.create(create_data)
print(f"Create response: {create_response.to_dict()}")
# Get user
user_id = "1"
get_response = user_api.get(user_id)
print(f"Get response: {get_response.to_dict()}")
# List users
list_response = user_api.list(page=1, per_page=10)
print(f"List response: {list_response.to_dict()}")
# Update user
update_data = {"name": "Jane Doe"}
update_response = user_api.update(user_id, update_data)
print(f"Update response: {update_response.to_dict()}")
# Delete user
delete_response = user_api.delete(user_id)
print(f"Delete response: {delete_response.to_dict()}")
API Versioning¶
# python/02-api-versioning.py
"""
API versioning strategies and implementation
"""
from typing import List, Dict, Any, Optional, Union
from dataclasses import dataclass
from enum import Enum
import re
from datetime import datetime
class APIVersion(Enum):
"""API version enumeration"""
V1 = "v1"
V2 = "v2"
V3 = "v3"
@dataclass
class VersionInfo:
"""API version information"""
version: str
release_date: datetime
deprecation_date: Optional[datetime] = None
is_deprecated: bool = False
is_sunset: bool = False
sunset_date: Optional[datetime] = None
class APIVersionManager:
"""API version manager"""
def __init__(self):
self.versions: Dict[str, VersionInfo] = {}
self.current_version = APIVersion.V1.value
self.default_version = APIVersion.V1.value
def register_version(self, version: str, release_date: datetime,
deprecation_date: Optional[datetime] = None,
sunset_date: Optional[datetime] = None) -> None:
"""Register API version"""
version_info = VersionInfo(
version=version,
release_date=release_date,
deprecation_date=deprecation_date,
is_deprecated=deprecation_date is not None,
sunset_date=sunset_date,
is_sunset=sunset_date is not None
)
self.versions[version] = version_info
def get_version_info(self, version: str) -> Optional[VersionInfo]:
"""Get version information"""
return self.versions.get(version)
def is_version_supported(self, version: str) -> bool:
"""Check if version is supported"""
version_info = self.get_version_info(version)
if not version_info:
return False
return not version_info.is_sunset
def get_supported_versions(self) -> List[str]:
"""Get list of supported versions"""
return [
version for version, info in self.versions.items()
if not info.is_sunset
]
def get_deprecated_versions(self) -> List[str]:
"""Get list of deprecated versions"""
return [
version for version, info in self.versions.items()
if info.is_deprecated and not info.is_sunset
]
def get_sunset_versions(self) -> List[str]:
"""Get list of sunset versions"""
return [
version for version, info in self.versions.items()
if info.is_sunset
]
class VersionedAPI:
"""Versioned API implementation"""
def __init__(self):
self.version_manager = APIVersionManager()
self.handlers: Dict[str, Dict[str, Any]] = {}
self.setup_versions()
def setup_versions(self) -> None:
"""Setup API versions"""
# Register versions
self.version_manager.register_version(
"v1",
datetime(2023, 1, 1),
deprecation_date=datetime(2024, 1, 1)
)
self.version_manager.register_version(
"v2",
datetime(2023, 6, 1)
)
self.version_manager.register_version(
"v3",
datetime(2024, 1, 1)
)
def register_handler(self, version: str, endpoint: str, handler: Any) -> None:
"""Register versioned handler"""
if version not in self.handlers:
self.handlers[version] = {}
self.handlers[version][endpoint] = handler
def get_handler(self, version: str, endpoint: str) -> Optional[Any]:
"""Get versioned handler"""
if version not in self.handlers:
return None
return self.handlers[version].get(endpoint)
def handle_request(self, version: str, endpoint: str, method: str, data: Dict[str, Any]) -> Dict[str, Any]:
"""Handle versioned request"""
# Check if version is supported
if not self.version_manager.is_version_supported(version):
return {
"error": "Unsupported API version",
"supported_versions": self.version_manager.get_supported_versions()
}
# Get handler
handler = self.get_handler(version, endpoint)
if not handler:
return {
"error": "Endpoint not found",
"version": version,
"endpoint": endpoint
}
# Execute handler
try:
result = handler(method, data)
return result
except Exception as e:
return {
"error": str(e),
"version": version,
"endpoint": endpoint
}
def get_version_headers(self, version: str) -> Dict[str, str]:
"""Get version-specific headers"""
version_info = self.version_manager.get_version_info(version)
if not version_info:
return {}
headers = {
"API-Version": version,
"API-Release-Date": version_info.release_date.isoformat()
}
if version_info.is_deprecated:
headers["API-Deprecation-Date"] = version_info.deprecation_date.isoformat()
headers["Warning"] = f"API version {version} is deprecated"
if version_info.is_sunset:
headers["API-Sunset-Date"] = version_info.sunset_date.isoformat()
headers["Warning"] = f"API version {version} is sunset"
return headers
# Usage examples
def example_api_versioning():
"""Example API versioning usage"""
# Create versioned API
api = VersionedAPI()
# Register handlers for different versions
def v1_user_handler(method: str, data: Dict[str, Any]) -> Dict[str, Any]:
return {"version": "v1", "method": method, "data": data}
def v2_user_handler(method: str, data: Dict[str, Any]) -> Dict[str, Any]:
return {"version": "v2", "method": method, "data": data, "enhanced": True}
api.register_handler("v1", "users", v1_user_handler)
api.register_handler("v2", "users", v2_user_handler)
# Handle requests
v1_response = api.handle_request("v1", "users", "GET", {})
print(f"V1 response: {v1_response}")
v2_response = api.handle_request("v2", "users", "GET", {})
print(f"V2 response: {v2_response}")
# Get version headers
v1_headers = api.get_version_headers("v1")
print(f"V1 headers: {v1_headers}")
# Get supported versions
supported = api.version_manager.get_supported_versions()
print(f"Supported versions: {supported}")
API Documentation¶
# python/03-api-documentation.py
"""
API documentation generation and OpenAPI integration
"""
from typing import List, Dict, Any, Optional, Union
from dataclasses import dataclass, asdict
from enum import Enum
import json
from datetime import datetime
class HTTPMethod(Enum):
"""HTTP methods"""
GET = "GET"
POST = "POST"
PUT = "PUT"
PATCH = "PATCH"
DELETE = "DELETE"
@dataclass
class APIParameter:
"""API parameter definition"""
name: str
type: str
required: bool = False
description: Optional[str] = None
example: Optional[Any] = None
default: Optional[Any] = None
@dataclass
class APIResponse:
"""API response definition"""
status_code: int
description: str
schema: Optional[Dict[str, Any]] = None
example: Optional[Any] = None
@dataclass
class APIEndpoint:
"""API endpoint definition"""
path: str
method: HTTPMethod
summary: str
description: str
parameters: List[APIParameter]
responses: List[APIResponse]
tags: List[str]
deprecated: bool = False
class APIDocumentation:
"""API documentation generator"""
def __init__(self, title: str, version: str, description: str):
self.title = title
self.version = version
self.description = description
self.endpoints: List[APIEndpoint] = []
self.tags: Dict[str, str] = {}
def add_tag(self, name: str, description: str) -> None:
"""Add API tag"""
self.tags[name] = description
def add_endpoint(self, endpoint: APIEndpoint) -> None:
"""Add API endpoint"""
self.endpoints.append(endpoint)
def generate_openapi(self) -> Dict[str, Any]:
"""Generate OpenAPI specification"""
openapi_spec = {
"openapi": "3.0.0",
"info": {
"title": self.title,
"version": self.version,
"description": self.description
},
"servers": [
{
"url": "https://api.example.com",
"description": "Production server"
},
{
"url": "https://staging-api.example.com",
"description": "Staging server"
}
],
"tags": [
{"name": name, "description": description}
for name, description in self.tags.items()
],
"paths": {}
}
# Group endpoints by path
paths = {}
for endpoint in self.endpoints:
if endpoint.path not in paths:
paths[endpoint.path] = {}
paths[endpoint.path][endpoint.method.value.lower()] = {
"summary": endpoint.summary,
"description": endpoint.description,
"tags": endpoint.tags,
"deprecated": endpoint.deprecated,
"parameters": [
{
"name": param.name,
"in": "query" if param.name in ["page", "per_page", "sort"] else "path",
"required": param.required,
"description": param.description,
"schema": {"type": param.type},
"example": param.example
}
for param in endpoint.parameters
],
"responses": {
str(response.status_code): {
"description": response.description,
"content": {
"application/json": {
"schema": response.schema or {"type": "object"},
"example": response.example
}
}
}
for response in endpoint.responses
}
}
openapi_spec["paths"] = paths
return openapi_spec
def generate_markdown(self) -> str:
"""Generate Markdown documentation"""
md_lines = [
f"# {self.title}",
f"**Version:** {self.version}",
f"**Description:** {self.description}",
"",
"## Endpoints",
""
]
# Group endpoints by tag
endpoints_by_tag = {}
for endpoint in self.endpoints:
for tag in endpoint.tags:
if tag not in endpoints_by_tag:
endpoints_by_tag[tag] = []
endpoints_by_tag[tag].append(endpoint)
for tag, endpoints in endpoints_by_tag.items():
md_lines.extend([
f"### {tag.title()}",
""
])
for endpoint in endpoints:
md_lines.extend([
f"#### {endpoint.method.value} {endpoint.path}",
f"**Summary:** {endpoint.summary}",
f"**Description:** {endpoint.description}",
""
])
if endpoint.parameters:
md_lines.extend([
"**Parameters:**",
""
])
for param in endpoint.parameters:
md_lines.extend([
f"- **{param.name}** ({param.type}){' - Required' if param.required else ''}",
f" - {param.description}",
""
])
if endpoint.responses:
md_lines.extend([
"**Responses:**",
""
])
for response in endpoint.responses:
md_lines.extend([
f"- **{response.status_code}** - {response.description}",
""
])
md_lines.append("---")
md_lines.append("")
return "\n".join(md_lines)
# Usage examples
def example_api_documentation():
"""Example API documentation usage"""
# Create API documentation
doc = APIDocumentation(
title="User Management API",
version="1.0.0",
description="API for managing users"
)
# Add tags
doc.add_tag("users", "User management operations")
doc.add_tag("authentication", "Authentication operations")
# Add endpoints
get_users_endpoint = APIEndpoint(
path="/users",
method=HTTPMethod.GET,
summary="List users",
description="Get a list of all users",
parameters=[
APIParameter("page", "integer", False, "Page number", 1),
APIParameter("per_page", "integer", False, "Items per page", 10)
],
responses=[
APIResponse(200, "Success", {"type": "array", "items": {"type": "object"}}),
APIResponse(400, "Bad Request"),
APIResponse(500, "Internal Server Error")
],
tags=["users"]
)
create_user_endpoint = APIEndpoint(
path="/users",
method=HTTPMethod.POST,
summary="Create user",
description="Create a new user",
parameters=[
APIParameter("name", "string", True, "User name"),
APIParameter("email", "string", True, "User email")
],
responses=[
APIResponse(201, "Created", {"type": "object"}),
APIResponse(400, "Bad Request"),
APIResponse(422, "Validation Error")
],
tags=["users"]
)
doc.add_endpoint(get_users_endpoint)
doc.add_endpoint(create_user_endpoint)
# Generate OpenAPI spec
openapi_spec = doc.generate_openapi()
print("OpenAPI spec:")
print(json.dumps(openapi_spec, indent=2))
# Generate Markdown documentation
markdown_doc = doc.generate_markdown()
print("\nMarkdown documentation:")
print(markdown_doc)
Error Handling and Validation¶
API Error Handling¶
# python/04-api-error-handling.py
"""
API error handling and validation patterns
"""
from typing import List, Dict, Any, Optional, Union
from dataclasses import dataclass
from enum import Enum
import json
from datetime import datetime
class APIErrorCode(Enum):
"""API error codes"""
VALIDATION_ERROR = "VALIDATION_ERROR"
AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR"
AUTHORIZATION_ERROR = "AUTHORIZATION_ERROR"
NOT_FOUND = "NOT_FOUND"
CONFLICT = "CONFLICT"
RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED"
INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
@dataclass
class APIError:
"""API error definition"""
code: APIErrorCode
message: str
details: Optional[Dict[str, Any]] = None
field: Optional[str] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
error_dict = {
"code": self.code.value,
"message": self.message
}
if self.details:
error_dict["details"] = self.details
if self.field:
error_dict["field"] = self.field
return error_dict
@dataclass
class APIErrorResponse:
"""API error response"""
status_code: int
errors: List[APIError]
request_id: Optional[str] = None
timestamp: datetime = None
def __post_init__(self):
if self.timestamp is None:
self.timestamp = datetime.utcnow()
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
response = {
"status_code": self.status_code,
"success": False,
"errors": [error.to_dict() for error in self.errors],
"timestamp": self.timestamp.isoformat()
}
if self.request_id:
response["request_id"] = self.request_id
return response
class APIValidator:
"""API request validator"""
def __init__(self):
self.validators: Dict[str, callable] = {}
self.setup_validators()
def setup_validators(self) -> None:
"""Setup validators"""
self.validators = {
"required": self._validate_required,
"email": self._validate_email,
"min_length": self._validate_min_length,
"max_length": self._validate_max_length,
"min_value": self._validate_min_value,
"max_value": self._validate_max_value,
"pattern": self._validate_pattern
}
def validate(self, data: Dict[str, Any], rules: Dict[str, Dict[str, Any]]) -> List[APIError]:
"""Validate data against rules"""
errors = []
for field, field_rules in rules.items():
value = data.get(field)
for rule_name, rule_value in field_rules.items():
if rule_name in self.validators:
error = self.validators[rule_name](field, value, rule_value)
if error:
errors.append(error)
return errors
def _validate_required(self, field: str, value: Any, required: bool) -> Optional[APIError]:
"""Validate required field"""
if required and (value is None or value == ""):
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' is required",
field=field
)
return None
def _validate_email(self, field: str, value: Any, is_email: bool) -> Optional[APIError]:
"""Validate email format"""
if is_email and value and "@" not in str(value):
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' must be a valid email",
field=field
)
return None
def _validate_min_length(self, field: str, value: Any, min_length: int) -> Optional[APIError]:
"""Validate minimum length"""
if value and len(str(value)) < min_length:
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' must be at least {min_length} characters",
field=field
)
return None
def _validate_max_length(self, field: str, value: Any, max_length: int) -> Optional[APIError]:
"""Validate maximum length"""
if value and len(str(value)) > max_length:
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' must be at most {max_length} characters",
field=field
)
return None
def _validate_min_value(self, field: str, value: Any, min_value: Union[int, float]) -> Optional[APIError]:
"""Validate minimum value"""
if value is not None and value < min_value:
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' must be at least {min_value}",
field=field
)
return None
def _validate_max_value(self, field: str, value: Any, max_value: Union[int, float]) -> Optional[APIError]:
"""Validate maximum value"""
if value is not None and value > max_value:
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' must be at most {max_value}",
field=field
)
return None
def _validate_pattern(self, field: str, value: Any, pattern: str) -> Optional[APIError]:
"""Validate pattern"""
if value and not re.match(pattern, str(value)):
return APIError(
code=APIErrorCode.VALIDATION_ERROR,
message=f"Field '{field}' does not match required pattern",
field=field
)
return None
class APIErrorHandler:
"""API error handler"""
def __init__(self):
self.validator = APIValidator()
def handle_validation_error(self, errors: List[APIError]) -> APIErrorResponse:
"""Handle validation errors"""
return APIErrorResponse(
status_code=400,
errors=errors
)
def handle_authentication_error(self, message: str = "Authentication required") -> APIErrorResponse:
"""Handle authentication errors"""
return APIErrorResponse(
status_code=401,
errors=[APIError(
code=APIErrorCode.AUTHENTICATION_ERROR,
message=message
)]
)
def handle_authorization_error(self, message: str = "Insufficient permissions") -> APIErrorResponse:
"""Handle authorization errors"""
return APIErrorResponse(
status_code=403,
errors=[APIError(
code=APIErrorCode.AUTHORIZATION_ERROR,
message=message
)]
)
def handle_not_found_error(self, resource: str = "Resource") -> APIErrorResponse:
"""Handle not found errors"""
return APIErrorResponse(
status_code=404,
errors=[APIError(
code=APIErrorCode.NOT_FOUND,
message=f"{resource} not found"
)]
)
def handle_conflict_error(self, message: str = "Resource conflict") -> APIErrorResponse:
"""Handle conflict errors"""
return APIErrorResponse(
status_code=409,
errors=[APIError(
code=APIErrorCode.CONFLICT,
message=message
)]
)
def handle_internal_error(self, message: str = "Internal server error") -> APIErrorResponse:
"""Handle internal errors"""
return APIErrorResponse(
status_code=500,
errors=[APIError(
code=APIErrorCode.INTERNAL_SERVER_ERROR,
message=message
)]
)
# Usage examples
def example_api_error_handling():
"""Example API error handling usage"""
# Create error handler
error_handler = APIErrorHandler()
# Create validator
validator = APIValidator()
# Validate data
data = {"name": "", "email": "invalid-email"}
rules = {
"name": {"required": True, "min_length": 2},
"email": {"required": True, "email": True}
}
errors = validator.validate(data, rules)
if errors:
error_response = error_handler.handle_validation_error(errors)
print(f"Validation error: {error_response.to_dict()}")
# Handle different error types
auth_error = error_handler.handle_authentication_error()
print(f"Auth error: {auth_error.to_dict()}")
not_found_error = error_handler.handle_not_found_error("User")
print(f"Not found error: {not_found_error.to_dict()}")
TL;DR Runbook¶
Quick Start¶
# 1. Basic API response
response = APIResponse(
status_code=200,
data={"id": 1, "name": "John"},
message="Success"
)
# 2. API versioning
version_manager = APIVersionManager()
version_manager.register_version("v1", datetime(2023, 1, 1))
# 3. API documentation
doc = APIDocumentation("My API", "1.0.0", "API description")
doc.add_endpoint(endpoint)
# 4. Error handling
error_handler = APIErrorHandler()
validation_error = error_handler.handle_validation_error(errors)
# 5. Request validation
validator = APIValidator()
errors = validator.validate(data, rules)
Essential Patterns¶
# Complete API design setup
def setup_api_design():
"""Setup complete API design environment"""
# API resource
api_resource = APIResource("users")
# Version manager
version_manager = APIVersionManager()
# Documentation
doc = APIDocumentation("User API", "1.0.0", "User management API")
# Error handler
error_handler = APIErrorHandler()
# Validator
validator = APIValidator()
print("API design setup complete!")
This guide provides the complete machinery for Python API design. Each pattern includes implementation examples, design strategies, and real-world usage patterns for enterprise API development.