Pure TypeScript
Outline
nestia
can use pure TypeScript type.
You know what? NestJS
needs triple duplicated DTO schema definitions. The 1st is defining TypeScript type, the 2nd and 3rd are calling decorator functions of class-validator
and @nestjs/swagger
. It’s not only annoying, but also error-prone. If you take any mistake on the 2nd or 3rd, it can’t be detected by TypeScript compiler. It will be detected only at runtime. Another words, it is not type safe.
Besides, nestia
needs only pure TypeScript type. You don’t need to define any extra schema like class-validator
or @nestjs/swagger
. Just define pure TypeScript type only (especially recommend to use interface
type), then nestia
will do all the rest.
Demonstration
If you’re confusing how DTO of NestJS
and nestia
are different, just see example codes below.
At first, look at the first (Triple duplicated NestJS DTO
) tab, and find the BbsArticle.files
property, enhanced by blue coloured blocks. Looking at the files
property, how do you feel? Just defining an array object type, you’ve to call 7 decorator functions. If you take any mistake when using the decorator like omitting isArray
property, it would be a critical runtime error.
Besides, nestia
needs only one line. Click the second (Pure Nestia DTO
) tab, and find the IAttachmentFile.files
property. Only one line being used, and IBbsArticle
and IAttachment
types are not even class, but just interface types. Comparing it to the first tab, how do you feel? Isn’t it more simple and readable?
This is the power of nestia
, with pure TypeScript type.
import { ApiProperty } from "@nestjs/swagger";
import {
ArrayNotEmpty,
Format,
IsArray,
IsObject,
IsOptional,
IsString,
Match,
MaxLength,
Type,
ValidateNested,
} from "class-validator";
export class BbsArticle {
@ApiProperty({
format: "uuid",
})
@IsString()
id!: string;
// DUPLICATED SCHEMA DEFINITION
// - duplicated function call + property type
// - have to specify `isArray` and `nullable` props by yourself
@ApiProperty({
type: () => AttachmentFile,
nullable: true,
isArray: true,
description: "List of attached files.",
})
@Type(() => AttachmentFile)
@IsArray()
@IsOptional()
@IsObject({ each: true })
@ValidateNested({ each: true })
files!: AttachmentFile[] | null;
@ApiProperty({
type: "string",
nullable: true,
minLength: 5,
maxLength: 100,
description: "Title of the article.",
})
@IsOptional()
@IsString()
title!: string | null;
@ApiProperty({
description: "Main content body of the article.",
})
@IsString()
body!: string;
@ApiProperty({
format: "date-time",
description: "Creation time of article",
})
@IsString()
created_at!: string;
}
export class AttachmentFile {
@ApiProperty({
type: "string",
maxLength: 255,
pattern: "^[a-zA-Z0-9-_]+$",
description: "File name.",
})
@Matches(/^[a-z0-9]+$/)
@MaxLength(255)
@IsString()
name!: string | null;
@ApiProperty({
type: "string",
nullable: true,
maxLength: 255,
pattern: "^[a-zA-Z0-9-_]+$",
description: "File extension.",
})
@Matches(/^[a-z0-9]+$/)
@MaxLength(8)
@IsOptional()
@IsString()
extension!: string | null;
@ApiProperty({
format: "url",
description: "URL of the file.",
})
@Format("uri")
@IsString()
url!: string;
}
AOT Compilation
Someone may be suspicious of the phrase “Pure TypeScript Type”.
“As you know, TypeScript types do not have any tangible instance when compiled to JS.
However, with only these fictitious TypeScript types, how can
nestia
validates types at runtime? Hownestia
builds swagger documents or SDK library with only these types? Are these things really possible without extra schema definition likeclass-validator
or@nestjs/swagger
?”
My answer is: “Yes, it is possible due to nestia
analyzes your server code, and performs AOT compilation”.
When compiling, nestia
travels your NestJS server codes, and analyzes DTO definitions. And then, nestia
writes optimal code to the compiled JavaScript file. In the @TypedBody()
case, nestia
transforms it to optimal validation code for the IBbsArticle.IStore
type. Also, nestia
transforms @TypedRoute.Post()
function to optimal JSON serialization code for the IBbsArticle
type.
Such compile time optimization is called AOT (Ahead of Time) compilation. And this is the secret why nestia
can do everything with only pure TypeScript type. Read below example codes, and just look how JavaScript file being compiled. Then you may understand why nestia
is much easier, and futhermore much faster.
- Runtime validator is 20,000x faster than
class-validator
- JSON serialization is 200x faster than
class-transformer
import { TypedBody, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { IBbsArticle } from "./IBbsArticle";
@Controller("bbs/articles")
export class BbsArticlesController {
@TypedRoute.Post() // 200x faster JSON serialization
public async store(
// 20,000x faster validation
@TypedBody() input: IBbsArticle.ICreate,
): Promise<IBbsArticle> {
return {
...input,
id: "2b5e21d8-0e44-4482-bd3e-4540dee7f3d6",
created_at: "2023-04-23T12:04:54.168Z",
};
}
}
Measured on AMD R9-7940HS, Rog Flow X13