The @Route decorator is used to define HTTP endpoints in your Framefox controllers. It provides automatic dependency injection, path parameter handling, and seamless integration with FastAPI.
Constructor
from framefox import Route
@Route(
path="/users/{user_id}",
name="get_user",
methods=["GET"],
response_model=UserResponse,
tags=["Users"]
)
Parameters
The URL path for the route. Supports path parameters using curly braces (e.g., /users/{user_id}).
A unique name identifier for the route. Used for route lookup and documentation.
List of HTTP methods this route responds to. Common values: ["GET"], ["POST"], ["PUT"], ["DELETE"], ["PATCH"].
Pydantic model class defining the response structure. Used for validation and OpenAPI documentation.
List of tags for grouping endpoints in OpenAPI documentation.
Dependency Injection
The @Route decorator automatically injects service dependencies into your route handlers based on type hints. The decorator inspects function parameters and resolves them from the dependency injection container.
How It Works
- Type Hint Detection: The decorator analyzes parameter type hints
- Service Resolution: Services are resolved from the controller’s DI container
- Automatic Injection: Resolved services are passed as keyword arguments
- Fallback Handling: Uses default values if injection fails
Excluded from Injection
The following parameter types are NOT injected and are passed through to FastAPI:
- Path parameters: Parameters matching
{param_name} in the route path
- FastAPI native types:
Request, Response, BackgroundTasks, WebSocket, Query, Path, Body, Form, File, UploadFile, Header, Cookie
- Pydantic models: Classes inheriting from
BaseModel
- Primitive types:
int, str, float, bool, bytes
Path Parameters
Path parameters are automatically extracted from the URL and passed to your handler:
@Route(path="/users/{user_id}/posts/{post_id}", name="get_post", methods=["GET"])
async def get_post(self, user_id: int, post_id: int):
# user_id and post_id are extracted from the URL
return {"user_id": user_id, "post_id": post_id}
Usage Examples
Basic GET Route
from framefox import Controller, Route
class UserController(Controller):
def __init__(self):
super().__init__(prefix="/users")
@Route(path="/", name="list_users", methods=["GET"])
async def list_users(self):
return {"users": []}
Route with Path Parameters
@Route(path="/{user_id}", name="get_user", methods=["GET"])
async def get_user(self, user_id: int):
return {"user_id": user_id, "name": "John Doe"}
Route with Dependency Injection
from framefox import Controller, Route, Injectable
@Injectable()
class UserService:
async def get_user(self, user_id: int):
return {"id": user_id, "name": "John Doe"}
class UserController(Controller):
def __init__(self):
super().__init__(prefix="/users")
@Route(path="/{user_id}", name="get_user", methods=["GET"])
async def get_user(self, user_id: int, user_service: UserService):
# user_service is automatically injected
return await user_service.get_user(user_id)
POST Route with Request Body
from pydantic import BaseModel
class CreateUserRequest(BaseModel):
name: str
email: str
class UserResponse(BaseModel):
id: int
name: str
email: str
@Route(
path="/",
name="create_user",
methods=["POST"],
response_model=UserResponse
)
async def create_user(self, data: CreateUserRequest, user_service: UserService):
# data is validated by Pydantic
# user_service is injected from DI container
user = await user_service.create(data)
return user
Route with FastAPI Types
from fastapi import Request, Header
@Route(path="/info", name="request_info", methods=["GET"])
async def request_info(
self,
request: Request,
user_agent: str = Header(None)
):
# Request and Header are FastAPI types, not injected
return {
"method": request.method,
"user_agent": user_agent
}
Multiple HTTP Methods
@Route(
path="/{user_id}",
name="user_endpoint",
methods=["GET", "PUT", "DELETE"]
)
async def handle_user(self, user_id: int, request: Request):
if request.method == "GET":
return {"action": "get", "user_id": user_id}
elif request.method == "PUT":
return {"action": "update", "user_id": user_id}
elif request.method == "DELETE":
return {"action": "delete", "user_id": user_id}
@Route(
path="/profile/{user_id}",
name="get_user_profile",
methods=["GET"],
response_model=UserProfileResponse,
tags=["Users", "Profiles"]
)
async def get_user_profile(
self,
user_id: int,
user_service: UserService,
profile_service: ProfileService
):
# Multiple services can be injected
user = await user_service.get(user_id)
profile = await profile_service.get(user_id)
return {**user, **profile}
Error Handling
If dependency injection fails and no default value is provided, a RuntimeError is raised:
@Route(path="/test", name="test", methods=["GET"])
async def test(self, required_service: RequiredService):
# If RequiredService cannot be resolved and has no default,
# RuntimeError: "Dependency injection failed for RequiredService"
pass
To provide a fallback:
@Route(path="/test", name="test", methods=["GET"])
async def test(self, optional_service: OptionalService = None):
# If OptionalService cannot be resolved, None is used
if optional_service:
return await optional_service.do_something()
return {"status": "service unavailable"}
See Also