Skip to content

Parameter Decorators

PyNest supports explicit HTTP parameter decorators for route handlers. Because Python does not have parameter-level decorator syntax, these decorators are used as default-value markers in the function signature.

from nest.common.decorators import Body, Headers, Param, Query
from nest.core import Controller, Get, Post


@Controller("/users")
class UsersController:
    @Post("/{user_id}")
    def update_user(
        self,
        user_id: int = Param("user_id"),
        payload: dict = Body(),
        trace_id: str = Headers("x-trace-id", default=None),
    ):
        return {"user_id": user_id, "payload": payload, "trace_id": trace_id}

    @Get("/")
    def list_users(self, page: int = Query("page", default=1)):
        return {"page": page}

Built-in Decorators

Body

Body() injects the full request body. Body("key") injects one embedded body field.

@Post("/")
def create(self, payload: CreateUserDto = Body()):
    return payload


@Post("/name")
def create_name(self, name: str = Body("name")):
    return {"name": name}

Param

Param("name") injects a path parameter. Param() injects all path parameters as a dictionary.

@Get("/{user_id}/posts/{post_id}")
def get_post(
    self,
    user_id: int = Param("user_id"),
    post_id: int = Param("post_id"),
):
    return {"user_id": user_id, "post_id": post_id}


@Get("/{user_id}")
def get_user(self, params: dict = Param()):
    return params

Query

Query("name") injects one query parameter. Query() injects all query parameters as a dictionary.

@Get("/search")
def search(
    self,
    q: str = Query("q"),
    limit: int = Query("limit", default=20),
):
    return {"q": q, "limit": limit}

Headers

Headers("name") injects one header. Headers() injects all headers as a dictionary.

@Get("/me")
def get_me(self, authorization: str = Headers("authorization")):
    return {"authorization": authorization}

Req, Res, Ip, and HostParam

Req() injects the FastAPI request, Res() injects the response object, Ip() injects the client IP address, and HostParam() injects the request hostname.

from fastapi import Request, Response
from nest.common.decorators import HostParam, Ip, Req, Res


@Get("/raw")
def raw(
    self,
    request: Request = Req(),
    response: Response = Res(),
    ip: str = Ip(),
    host: str = HostParam(),
):
    response.headers["x-client-ip"] = ip
    return {"path": request.url.path, "host": host}

Pipes

Decorators accept one or more pipe callables after the source name. A pipe can be a callable or a class/instance exposing transform(value).

def parse_int(value):
    return int(value)


class TrimPipe:
    def transform(self, value):
        return value.strip()


@Get("/{id}")
def get_item(
    self,
    item_id=Param("id", parse_int),
    q: str = Query("q", TrimPipe),
):
    return {"item_id": item_id, "q": q}

Custom Decorators

Use createParamDecorator(factory) to build reusable domain-specific parameter decorators. The factory receives data and an ExecutionContext.

from nest.common.decorators import createParamDecorator


CurrentUser = createParamDecorator(
    lambda data, ctx: ctx.switch_to_http().get_request().state.user
)

TenantId = createParamDecorator(
    lambda data, ctx: ctx.switch_to_http().get_request().headers.get("x-tenant-id")
)

UserProperty = createParamDecorator(
    lambda data, ctx: getattr(ctx.switch_to_http().get_request().state.user, data)
)


@Get("/profile")
def profile(
    self,
    user=CurrentUser(),
    email: str = UserProperty("email"),
    tenant_id: str = TenantId(),
):
    return {"user": user, "email": email, "tenant_id": tenant_id}

Routes without parameter decorators continue to use FastAPI's normal signature binding.