Skip to main content
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

path
str
required
The URL path for the route. Supports path parameters using curly braces (e.g., /users/{user_id}).
name
str
required
A unique name identifier for the route. Used for route lookup and documentation.
methods
list[str]
required
List of HTTP methods this route responds to. Common values: ["GET"], ["POST"], ["PUT"], ["DELETE"], ["PATCH"].
response_model
Type
default:"None"
Pydantic model class defining the response structure. Used for validation and OpenAPI documentation.
tags
list[str]
default:"[]"
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

  1. Type Hint Detection: The decorator analyzes parameter type hints
  2. Service Resolution: Services are resolved from the controller’s DI container
  3. Automatic Injection: Resolved services are passed as keyword arguments
  4. 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 with Tags and Response Model

@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

Build docs developers (and LLMs) love