@TypedQuery
export function TypedQuery(): ParameterDecorator;
export namespace TypedQuery {
export function Body(): ParameterDecorator; // application/x-www-form-urlencoded body
export function Get(path?: string): MethodDecorator;
export function Post(path?: string): MethodDecorator;
export function Put(path?: string): MethodDecorator;
export function Patch(path?: string): MethodDecorator;
export function Delete(path?: string): MethodDecorator;
}Family of decorators for the URL query string.
@TypedQuery()β parameter decorator. Parses the query into a typed DTO, validates each field, coerces strings intonumber/boolean/bigint/null.@TypedQuery.Body()β parameter decorator forapplication/x-www-form-urlencodedrequest bodies. Same parsing semantics, different content type.@TypedQuery.${method}(path)β route decorator companion toTypedRoute.${method}. Pairs naturally with@TypedQuery.Body()for form-urlencoded POSTs.
Drop-in replacement for @Query() from @nestjs/common β but @Query() only sees strings; @TypedQuery() gives you the type your handler declared.
Basic usage β @TypedQuery()
import { TypedQuery, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { tags } from "typia";
interface IListQuery {
page?: number & tags.Type<"uint32"> & tags.Minimum<1>;
limit?: number & tags.Type<"uint32"> & tags.Minimum<1> & tags.Maximum<100>;
sort?: "newest" | "oldest";
q?: string;
}
@Controller("articles")
export class ArticlesController {
@TypedRoute.Get()
public async list(
@TypedQuery() query: IListQuery,
): Promise<IPage<IArticle>> {
const page = query.page ?? 1;
const limit = query.limit ?? 20;
// β¦
}
}A request to GET /articles?page=2&limit=50&sort=newest calls the handler with { page: 2, limit: 50, sort: "newest" }. A request to GET /articles?page=abc returns 400 before the handler runs.
Things @TypedQuery() does that vanilla @Query() does not:
- Coerce
"42"β42when the field is typednumber. - Coerce
"true"/"false"βboolean. - Coerce
"null"βnull,"123n"βbigint. - Validate
tags.*constraints (Format,MinLength,Maximum, β¦). - Accept arrays via repeated keys (
?tag=ts&tag=nodeβtags: ["ts", "node"]).
Nested DTOs and Swagger decomposition
A flat DTO is the typical case, but nested objects work too:
interface ISearchQuery {
filter?: {
section?: string;
after?: string & tags.Format<"date-time">;
};
}By default, Swagger documents the whole object as a single query schema. To decompose it into one query parameter per leaf field (the more conventional OpenAPI style), set swagger.decompose: true in nestia.config.ts:
const config: INestiaConfig = {
input: () => NestFactory.create(AppModule),
output: "src/api",
swagger: {
output: "dist/swagger.json",
decompose: true,
},
};The runtime parsing is identical either way β decompose only affects the generated OpenAPI schema.
@TypedQuery.Body() β form-urlencoded bodies
import { TypedQuery, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { tags } from "typia";
interface ILoginForm {
email: string & tags.Format<"email">;
password: string;
remember?: boolean;
}
@Controller("auth")
export class AuthController {
@TypedQuery.Post("login")
public async login(
@TypedQuery.Body() form: ILoginForm,
): Promise<{ token: string }> { ... }
}Use this for legacy HTML form posts and OAuth token endpoints where the body is application/x-www-form-urlencoded. For multipart/form-data (file uploads), use @TypedFormData.
When to use plain @Query instead
Reach for the vanilla decorator when:
- You need raw strings without coercion or validation (rare).
- A single route was working with
@Query()and you donβt want the migration churn.
Vanilla query parameters are invisible to @nestia/sdkβs generators.
Common pitfalls
Array params need repeated keys. ?tags=a,b,c does not parse as ["a","b","c"]. Send ?tags=a&tags=b&tags=c instead, or declare the field as string and split it yourself.
Trailing = for boolean. Browsers sometimes send ?remember (no value) to mean true. @TypedQuery() follows form-urlencoded semantics β declare remember: boolean and send ?remember=true.
Big numbers. Query strings carry no type information, so 42.0000001 round-trips as number. Use bigint and send ?x=42n if you need integer precision.
See also
- TypedBody β JSON request bodies.
- TypedFormData β multipart bodies (file uploads).
- TypedRoute β typed HTTP method decorators.