Outline
export function TypedQuery(): ParameterDecorator;
export namespace TypedQuery {
export function Body(): ParameterDecorator;
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;
}
Decorators for query parameters.
What the query parameters are?
This is the query parameters!
name=Samchon&age=20&sex=male
@TypedQuery()
is not essential for Swagger Documents or SDK Library building.
Therefore, it is not a matter to use @TypedQuery()
or @Query()
of the original NestJS.
@TypedQuery()
export function TypedQuery(): ParameterDecorator;
Type safe URL query decorator.
@TypedQuery()
is a decorator parsing URL query.
It's almost same with original @Query()
function of NestJS
, but @TypedQuery()
is more stable and general.
While NestJS
does not support query type validation, @TypedQuery()
validates the request query type and throws 400 bad request error when mismatched. Also, while NestJS
does not support property type (@Query()
only supports string
typed properties), @TypedQuery()
can define variable property types like bigint
, number
or boolean
.
import { TypedQuery, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { tags } from "typia";
import { IBbsArticle } from "./IBbsArticle";
import { IPage } from "./IPage";
@Controller("bbs/articles")
export class BbsArticlesController {
@TypedRoute.Get()
public async index(
@TypedQuery() query: IPage.IRequest
): Promise<IPage<IBbsArticle.ISummary>> {
return {
pagination: {
current: query.page ?? 1,
limit: query.limit ?? 100,
records: 0,
pages: 0,
},
data: [],
};
}
}
Just call @TypedQuery()
function on the query parameter, that's all.
Nestia
will analyze your type (IPage.IRequest
), and writes optimal code for the target type, in the compilation level. If you click the "Compiled JavaScript" file tab of above and fine enhanced lines by blue lines, you can see the optimal parsing and validation code.
Such optimization is called AOT (Ahead of Time) compilation, and it is the secret of @TypedQuery
.
TypedQuery.Body()
export namespace TypedQuery {
export function Body(): ParameterDecorator;
}
Request body decorator of application/x-www-form-urlencoded
format.
If you call @TypedQuery.Body()
decorator function on a specific parameter, the parameter will be parsed from the request body as application/x-www-form-urlencoded
format. Otherwise, you want to declare a application/json
format response body, use @TypedBody()
decorator function instead.
import { TypedQuery } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { IBbsArticle } from "@api/lib/structures/IBbsArticle";
@Controller("bbs/articles")
export class BbsArticlesController {
@TypedQuery.Post()
public async store(
@TypedQuery.Body() body: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
id: "00000000-0000-0000-0000-000000000000",
writer: "Samchon",
title: body.title,
body: body.body,
created_at: new Date().toISOString(),
};
}
}
TypedQuery.Post()
export namespace TypedQuery {
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;
}
Route decorators of application/x-www-form-urlencoded
format response body.
If you call one of below decorator functions on a method, the method will return application/x-www-form-urlencoded
format response body. Otherwise, you want to declare a application/json
format response body, use @TypedRoute.Post()
decorator function instead.
@TypedQuery.Get()
@TypedQuery.Post()
@TypedQuery.Put()
@TypedQuery.Patch()
@TypedQuery.Delete()
import { TypedQuery } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { IBbsArticle } from "@api/lib/structures/IBbsArticle";
@Controller("bbs/articles")
export class BbsArticlesController {
@TypedQuery.Post()
public async store(
@TypedQuery.Body() body: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
id: "00000000-0000-0000-0000-000000000000",
writer: "Samchon",
title: body.title,
body: body.body,
created_at: new Date().toISOString(),
};
}
}
Special Tags
You can enhance validation logic, of @TypedQuery()
, through comment tags.
You know what? @TypedQuery()
utilizes typia.assert<T>()
(opens in a new tab) function for query data validation, and the typia.assert<T>()
(opens in a new tab) function supports additional type checking logics through comment tags. For reference, "Type Tag" means a intersection type with atomic type and special tag type of typia
like number & tags.Type<"uint32">
, and "Comment Tag" means a comment starting from @
symbol following @${name} ${value}
format.
With those type and comment tags, you can add additional validation logics. If you want to add a custom validation logic, you also can do it. Read below Guide Docments of typia (opens in a new tab), and see the example code. You may understand how to utilize such type and comment tags, in a few minutes.
import typia, { tags } from "typia";
export const checkSpecialTag = typia.createIs<SpecialTag>();
interface SpecialTag {
int32: number & tags.Type<"int32">;
range?: number & tags.ExclusiveMinimum<19> & tags.Maximum<100>;
minLength: string & tags.MinLength<3>;
pattern: string & tags.Pattern<"^[a-z]+$">;
date: null | (string & tags.Format<"date">);
ip: string & (tags.Format<"ipv4"> | tags.Format<"ipv6">);
uuids: Array<string & tags.Format<"uuid">> &
tags.MinItems<3> &
tags.MaxItems<100>;
}
Restriction
When using @TypedQuery()
, you've to follow such restrction.
At first, type of @TypedQuery()
must be a pure object type. It does not allow union type. Also, nullable and undefindable types are not allowed, either. Note that, query parameter type must be a sole object type without any extra definition.
At next, type of properties must be atomic, or array of atomic type. In the atomic type case, the atomic type allows both nullable and undefindable types. However, mixed union atomic type like string | number
or "1" | "2" | 3
are not allowed. Also, the array type does not allow both nullable and undefindable types, either.
boolean
number
bigint
string
export interface SomeQueryDto {
//----
// ATOMIC TYPES
//----
// ALLOWED
boolean: boolean;
number: number;
string: string;
bigint: bigint;
optional_number?: number;
nullable_string: string | null;
literal_union: "A" | "B" | "C" | "D";
// NOT ALLOWED
mixed_union: string | number | boolean;
mixed_literal: "A" | "B" | 3;
//----
// ARRAY TYPES
//----
// ALLOWED
nullable_element_array: (string | null)[];
string_array: string[];
number_array: number[];
literal_union_array: ("A" | "B" | "C")[];
literal_tuple: ["A", "B", "C"];
// NOT ALLOWED
optional_element_array: (string | undefined)[];
optional_array: string[] | undefined;
nullable_array: string[] | null;
union_atomic_array: (string | number)[];
mixed_literal_array: ("A", "B", 3)[];
mixed_tuple: ["A", "B", 3];
}