Routing
Route Registration
from neutron import Router
router = Router()
@router.get("/users")
async def list_users() -> list[User]: ...
@router.post("/users")
async def create_user(input: CreateUser) -> User: ...
@router.put("/users/{user_id}")
async def update_user(user_id: int, input: UpdateUser) -> User: ...
@router.patch("/users/{user_id}")
async def patch_user(user_id: int, input: PatchUser) -> User: ...
@router.delete("/users/{user_id}")
async def delete_user(user_id: int) -> None: ...
Parameter Extraction
Parameters are automatically extracted based on type annotations:
Path Parameters
@router.get("/users/{user_id}/posts/{post_id}")
async def get_post(user_id: int, post_id: int) -> Post:
# Extracted from URL path
...
Request Body
from pydantic import BaseModel
class CreateUser(BaseModel):
name: str
email: str
age: int | None = None
@router.post("/users")
async def create_user(input: CreateUser) -> User:
# Parsed from JSON body, validated by Pydantic
...
Query Parameters
from neutron import Query
class Filters(BaseModel):
page: int = 1
per_page: int = 20
q: str | None = None
@router.get("/users")
async def list_users(query: Query[Filters]) -> list[User]:
# ?page=2&per_page=50&q=alice
...
Headers
from neutron import Header
class AuthHeaders(BaseModel):
authorization: str
@router.get("/protected")
async def protected(headers: Header[AuthHeaders]) -> dict:
...
Raw Request
from starlette.requests import Request
@router.get("/custom")
async def custom(request: Request) -> dict:
body = await request.json()
...
Route Groups
api = router.group("/api")
@api.get("/items")
async def list_items() -> list[Item]: ...
@api.post("/items")
async def create_item(input: CreateItem) -> Item: ...
Dependency Injection
from neutron import Depends
async def get_db():
return request.app.state.db
async def get_current_user(db = Depends(get_db)):
# Resolved recursively
...
@router.get("/me")
async def me(user = Depends(get_current_user)) -> User:
return user
Response Serialization
Return types are automatically serialized:
| Return Type | Response |
|-------------|----------|
| Pydantic model | JSON (200) |
| list[Model] | JSON array (200) |
| dict | JSON (200) |
| None | 204 No Content |
| str | text/plain (200) |
| Starlette Response | passed through |
Error Handling
from neutron.error import not_found, bad_request, validation_error
@router.get("/users/{user_id}")
async def get_user(user_id: int) -> User:
user = await db.find(user_id)
if not user:
raise not_found("User not found")
return user
RFC 7807 errors: bad_request, unauthorized, forbidden, not_found, conflict, validation_error, rate_limited, internal_error.