Routing¶
URL Routing
Unchained provides a powerful routing system that allows you to define API endpoints with type safety and dependency injection.
Basic Routing¶
HTTP Methods¶
Request Body¶
Unchained leverages Pydantic for powerful, automatic request body validation. Simply define your data structure as a Pydantic model and use it as a parameter in your route function.
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
description: str = None
tags: list[str] = []
@app.post("/items")
def create_item(item: Item):
# The item parameter will automatically be populated from the request JSON body
# Validation happens automatically based on the Pydantic model
return {"item_id": 123, "item": item}
from pydantic import BaseModel, Field, EmailStr
class User(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
age: int = Field(..., ge=18, description="Age must be at least 18")
@app.post("/users")
def create_user(user: User):
# Validation will ensure:
# - username is between 3-50 characters
# - email is a valid email format
# - age is at least 18
return {"user_id": 123, "user": user}
When validation fails, Unchained automatically returns a 422 Unprocessable Entity response with details about the validation errors:
Request and Response¶
Modular Routing¶
Path Parameters¶
from pydantic import BaseModel
class ItemID(BaseModel):
id: int
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
if not isinstance(v, int):
raise ValueError("Item ID must be an integer")
if v < 1:
raise ValueError("Item ID must be positive")
return cls(id=v)
@app.get("/items/{item_id}")
def get_item(item_id: ItemID):
return {"item_id": item_id.id}
Headers¶
from typing import Annotated
from unchained.dependencies.header import Header
@app.get("/items")
def get_items(user_agent: Annotated[str, Header()]):
return {"user_agent": user_agent}
Query Parameters¶
Access query parameters directly from the request object:
@app.get("/items")
def get_items(request: Request):
page = int(request.query_params.get("page", 1))
limit = int(request.query_params.get("limit", 10))
return {
"items": [],
"page": page,
"limit": limit
}
Best Practices¶
- Type Safety: Always use type hints for parameters
- Validation: Use Pydantic models for request/response validation
- Modularity: Split routes into logical routers
- Documentation: Add docstrings to describe endpoint behavior