@Controller¶
Spec
Full specification: specs/controller.md
· Item: AJ-10
Purpose¶
@Controller(prefix) is a class decorator that binds HTTP routes to a
class with a shared path prefix. It stamps __ajolopy_route_prefix__ on
the class and tells mount_routes(app, [...]) to concatenate the prefix
with each method-level path before registering the route.
Method-level decorators (@Get, @Post, @Put, @Patch, @Delete)
already stamp per-method metadata; @Controller layers the class-level
prefix on top so you write @Controller("/users") once instead of
repeating /users in every method.
Reach for @Controller whenever you need non-streaming HTTP endpoints —
for streaming, use @Stream.
Signature¶
Quick example¶
from typing import Annotated
from pydantic import BaseModel
from ajolopy import Controller, Get, Post
from ajolopy.http import Body, Param, create_app
from ajolopy.routes import mount_routes
class CreateUserDto(BaseModel):
email: str
name: str
@Controller("/users")
class UsersController:
@Get("/")
async def list_users(self) -> dict[str, list[object]]:
return {"items": []}
@Get("/{user_id}")
async def get_user(self, user_id: Annotated[str, Param()]) -> dict[str, str]:
return {"id": user_id}
@Post("/")
async def create_user(
self, body: Annotated[CreateUserDto, Body()]
) -> dict[str, str]:
return {"id": "u_1", "email": body.email}
app = create_app()
mount_routes(app, [UsersController])
# Registered routes: GET /users/, GET /users/{user_id}, POST /users/
Kwargs¶
| Kwarg | Type | Default | Description |
|---|---|---|---|
prefix |
str |
"" |
Path prefix concatenated with every method-level path. Empty string is legal. |
Escape hatches¶
- Root-level controllers.
@Controller("")produces unprefixed routes — useful for the app's root controller. - Plain classes. Method decorators (
@Getetc.) work without a@Controllerwrapper for ad-hoc routes;mount_routesregisters them with an empty prefix. - DI for controllers.
@Moduleregisters controllers as singletons in the container; the constructor type hints drive dependency injection just like@Injectableproviders.
Common gotchas¶
- The
prefixargument is required —@Controllerwithout()is not supported (unlike@Injectable). Empty prefix is fine, no parentheses are not. - Trailing slashes are stripped at decoration time:
@Controller("/users/")and@Controller("/users")produce the same effective prefix. Method-level paths keep their own trailing-slash semantics. __ajolopy_route_prefix__is not inherited by subclasses. Mirror the@Module/@Injectablerule and re-decorate explicitly.- Re-decoration raises
ControllerConfigError. A class has exactly one prefix. mount_routesjoins prefix + method-path with plain string concatenation. Double slashes in the method path are the user's bug; the framework does not deduplicate.
See also¶
@Stream— for streaming HTTP endpoints.@UseGuards— gate controllers behind a guard chain.@Module— register controllers viacontrollers=.- Spec:
specs/controller.md.