Request parameters
Inject the body, query, path parameters, headers, cookies, request, or response settings into a handler.
Parameter decorators inject one piece of the request into a route handler. Each accepts an optional property name that selects a single value instead of the whole part.
| Decorator | Injects |
|---|---|
@Body() | The validated request body. |
@Query("term") | The parsed query string, or one entry. |
@Param("id") | Path parameters, or one named parameter. |
@Headers("x-tenant") | Request headers, or one named header. |
@Cookie("session") | Cookies, or one cookie's value. |
@Req() | The native Request. |
@Res() | The mutable response settings. |
@Ctx() | The whole platform request context. |
All eight are exported from @aponiajs/common.
import { Body, Controller, Get, Headers, Param, Post, Query } from "@aponiajs/common";
@Controller("users")
export class UserController {
@Post()
create(@Body() body: { name: string }, @Headers("x-tenant") tenant: string) {
return { tenant, name: body.name };
}
@Get(":id")
findOne(@Param("id") id: string, @Query("expand") expand: string | undefined) {
return { id, expand };
}
}Named and whole forms
A decorator without a name injects the whole part; with a name it selects one property. Both forms can appear in the same handler:
@Post("body", createItemSchema)
readBody(@Body() body: CreateItem, @Body("name") name: string) {
return { body, name };
}@Cookie("session") is the one special case: it returns the cookie's value
rather than the cookie object Elysia stores.
Response settings
@Res() injects the mutable response settings — status, headers, and redirect —
rather than a Node-style response object:
import { Controller, Get, Res, type RouteResponseSettings } from "@aponiajs/common";
@Controller("reports")
export class ReportController {
@Get()
read(@Res() response: RouteResponseSettings) {
response.headers["x-source"] = "reports";
response.status = 201;
return { written: true };
}
}interface RouteResponseSettings {
status?: number | string;
headers: Record<string, string | number | undefined>;
redirect?: string;
}The whole context
A handler with no parameter decorators may declare one unannotated parameter to
receive the context. @Ctx() does the same explicitly, which is useful when
other parameters are present. Keep a handler parameterless when it needs no
request data; the compiled route then avoids materializing unused context
fields.
Annotate the context with RouteContext<typeof schema> from
@aponiajs/common to stay platform-neutral, or with ElysiaRouteContext from
@aponiajs/platform-elysia to keep Elysia's own status, set, cookie,
store, and redirect typed:
import { Controller, Ctx, Post } from "@aponiajs/common";
import { type ElysiaRouteContext } from "@aponiajs/platform-elysia";
import { z } from "zod";
const createUser = { body: z.object({ name: z.string().min(2) }) };
@Controller("users")
export class UserController {
@Post("/", createUser)
create(@Ctx() context: ElysiaRouteContext<typeof createUser>) {
context.set.headers["x-created"] = "1";
return context.body.name === "root"
? context.status(403, "forbidden")
: { name: context.body.name };
}
}The platform-neutral RouteContext<TSchema> exposes body, query, params,
headers, request, path, and set. Slots covered by a validator are typed
from that validator's output; uncovered slots fall back to string records.
To type what a native Elysia plugin adds to the context, see typed plugin context.
Argument binding rules
- Arguments are placed at the decorated parameter index, so decorators may be applied in any order.
- An undecorated parameter beside decorated ones receives
undefined. - A named selection on a non-object part resolves to
undefinedrather than throwing. - Parameter decorators may only decorate route handler parameters; using one on
a constructor parameter throws a
TypeError.