DEV Community

Cover image for [Nestia] Boost up NestJS server much faster and easier (maximum 20,000x faster + tRPC similar)
Jeongho Nam
Jeongho Nam

Posted on • Edited on

[Nestia] Boost up NestJS server much faster and easier (maximum 20,000x faster + tRPC similar)

Preface

Nestia Logo

As I've promised in the previous Typia Series Articles (dev.to), I'll introduce you my new library nestia, which can make NestJS much faster and easier.

With my new library nestia, you can boost up your NestJS server performance dramatically, just by writing only one line per each API function. Also, you can build SDK library, so you can help your fellow client developers to be much convenient.

Let's see nestia, and imagine how much your NestJS server would be powerful.

  • Only one line required, with pure TypeScript type
  • Runtime validator is 20,000x faster than class-validator
  • JSON serialization is 200x faster than class-transformer
  • SDK is similar with tRPC, but much advanced

SDK Sample

Left is NestJS server code, and right is client code utilizing SDK.

You can write client code much easily and safely.


Pure TypeScript Type

In NestJS, you have to define DTO by utilizing those libraries:

  • class-validator
  • class-transformer
  • @nestjs/swagger

I hate those libraries, because they force developer to define duplicated types like below. As long as you are using them following formal guide of NestJS, you never can avoid to duplicated type definitions in both TypeScript type and decorators.

Let's see how those libraries make DTO ugly:



export class BbsArticle {
    @IsString()
    @ApiProperty({
        format: "uuid",
    })
    id!: string;

    // DUPLICATED SCHEMA DEFINITION
    // - duplicated function call + property type
    // - have to specify `isArray` and `nullable` props by yourself
    @IsArray()
    @IsObject()
    @ValidateNested()
    @Type(() => AttachmentFile)
    @ApiProperty({
        type: () => AttachmentFile,
        nullable: true,
        isArray: true,
        description: "List of attached files.",
    })
    files!: AttachmentFile[] | null;

    @IsString()
    @IsOptional()
    @ApiProperty({
        type: "string",
        nullable: true,
        minLength: 5,
        maxLength: 100,
        description: "Title of the article.",
    })
    title!: string | null;

    @IsString()
    @ApiProperty({
        description: "Main content body of the article."
    })
    body!: string;

    @IsString()
    @ApiProperty({
        format: "date-time",
        description: "Creation time of article",
    })
    created_at!: string;
}

export class AttachmentFile {
    @IsString()
    @IsOptional()
    @ApiProperty({
        type: "string",
        nullable: true,
        maxLength: 255,
        pattern: "^[a-zA-Z0-9-_]+$",
        description: "File name.",
    })
    name!: string | null;

    @IsString()
    @IsOptional()
    @ApiProperty({
        type: "string",
        nullable: true,
        maxLength: 255,
        pattern: "^[a-zA-Z0-9-_]+$",
        description: "File extension.",
    })
    extension!: string | null;

    @IsString()
    @ApiProperty({
        format: "url",
        description: "URL of the file.",
    })
    url!: string;
}


Enter fullscreen mode Exit fullscreen mode

In contrary, with nestia, you can define DTO with pure TypeScript type only. Thus, you don't need to be suffered from duplicated type definitions. Furthermore, even supports nestia interface typed DTO.

Let's see how nestia changes DTO so much easier and beautiful:



export interface IBbsArticle {
    /**
     * Primary Key.
     * 
     * @format uuid
     */
    id: string;

    /**
     * List of attached files.
     */
    files: IAttachmentFile[] | null;

    /**
     * Title of the article.
     * 
     * @minLength 5
     * @maxLength 100
     */
    title: string | null;

    /**
     * Main content body of the article.
     */
    body: string;

    /**
     * Creation time of article.
     * 
     * @format date-time
     */
    created_at: string;
}

export interface IAttachmentFile {
    /**
     * File name.
     * 
     * @pattern ^[a-z0-9]+$
     * @maxLength 255
     */
    name: string | null;

    /**
     * File extension.
     * 
     * @pattern ^[a-z0-9]+$
     * @maxLength 8
     */
    extension: string | null;

    /**
     * URL of the file.
     * 
     * @format uri
     */
    url: string;
}


Enter fullscreen mode Exit fullscreen mode

Superfast Validation

If you have seen my previous Typia Series Articles (dev.to), reading above #Pure TypeScript Type section, you may suspect that nestia is using typia.

Yes, nestia is utilizing typia, and it is the secret why nestia can use pure TypeScript type as DTO. Also, as nestia is using typia, it can provide superfast validation.

Do you remember? Validation speed of typia was maximum 20,000x faster than class-validator what NestJS is using. Such super-fast validation speed also can be applied to your NestJS server, just by using nestia.

Now, change your class-validator based DTO classes to pure TypeScript type, and utilize @TypedBody() function like below. Then, your NestJS server's validation speed (about request body data) would be 20,000x times faster.

It seems very easy and wonderful, isn't it?



import { TypedBody, TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";

import { IBbsArticle } from "./IBbsArticle";

@Controller("bbs/articles")
export class BbsArticlesController {
    @TypedRoute.Post()
    public async store(
        // 20,000x faster validation
        @TypedBody() input: IBbsArticle.IStore
    ): Promise<IBbsArticle> {
        return {
            ...input,
            id: "2b5e21d8-0e44-4482-bd3e-4540dee7f3d6",
            created_at: "2023-04-23T12:04:54.168Z",
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Assert Function Benchmark

Measured on Intel i5-1135g7, Surface Pro 8


Superfast Serialization

nestia is using typia. Therefore, in the same reason with #Superfast Validation, JSON serialization speed also can be 200x faster than class-transformer what NestJS is using.

Just use @TypedRoute.${method}() decorator on your API function. Then, your NestJS server's JSON serialization speed (about response body data) would be 200x times faster.

If you're wondering how JSON serialization speed in the server side, look at the server benchmark graph of below. Just by adapting nestia and utilizing its @TypedRoute.${method}() function, your NestJS server can accept maximum 30x more requests.

Most operations in NodeJS server are asynchronously executed in background thread, what are called "event based non-blocking I/O model". However, JSON serialization is a synchronous operation running on the main thread. Therefore, if the JSON serialization speed is slow, it makes the entire server program slow.

It seems very powerful, isn't it?



import { TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";

import { IBbsArticle } from "./IBbsArticle";

@Controller("bbs/articles")
export class BbsArticlesController {
    // 200x faster JSON serialization
    @TypedRoute.Get("random")
    public async random(): Promise<IBbsArticle> {
        return {
            id: "2b5e21d8-0e44-4482-bd3e-4540dee7f3d6",
            title: "Hello nestia users",
            body: "Just use `TypedRoute.Get()` function like this",
            created_at: "2023-04-23T12:04:54.168Z",
            files: [],
        };
    }
}


Enter fullscreen mode Exit fullscreen mode

Stringify Function Benchmark

Measured on Intel i5-1135g7, Surface Pro 8

Server Benchmark

Measured on Intel i5-1135g7, Surface Pro 8


SDK generation

Do you know tRPC? If you develop your NodeJS backend server with it, you don't need to deliver any Swagger Documents to your fellow client developers. You just can send an interaction library for them.

By the way, tRPC is good for building toy projects, but not suitable for developing enterprise level backend server. In such reason, many NestJS lovers had requested tRPC team to support NestJS. But considering principle of tRPC, it never can support NestJS.

Instead, I support SDK generation for NestJS through my new library nestia. Just run npx nestia sdk command, then nestia will analyze your NestJS backend server code, and generates optimal SDK library in the compilation level.

Look at below gif image, and imagine how SDK would be helpful for your fellow client developers. Your fellow client developers will just import the SDK library, and call API functions with auto completion.

If they take any mistake, compiler will detect it, therefore you don't need to be suffered from runtime error by mistakes.

SDK Sample

Left is NestJS server code, and right is client code utilizing SDK.

You can write client code much easily and safely.

Top comments (15)

Collapse
 
cmcnicholas profile image
Craig McNicholas

Been interesting following your articles, I think you're doing a great job but the title is a bit misleading. This is making serialising and validating a hell of a lot faster but that is only one aspect of the "speed" most people will think of, your major resource/time intensive operations tend to be connected to other services like database read/write, file storage, cache access etc. So I'm not sure 20k times faster is really applicable in the real world, for most they will shave a few milliseconds off their APIs, but this is still great when you think about heavily active apis and the amount of time "wasted".

Great stuff, hope I get chance to give typia a go sometime.

Collapse
 
samchon profile image
Jeongho Nam • Edited

Titles are always difficult for me.

A title listing all the core features of Typia and nestia would be too long. In contrary, when I tried to find a great word to summarize them all at once, there was no other word like that.

At first, I tried to appeal to the SDK or pure typescript type DTO, but it was too unfamiliar and unfamiliar to people. Therefore, I explored elements that could introduce my library well, and studied how other open sources introduced themselves.

What I learned from studying other open source cases is that most open sources emphasize speed first when appealing, even if they have various advantages other than speed. Speed is clearly revealed as an objective figure, and these kinds of performance improvements seem to stimulate the developers' desire.

Because of that, I first appeal for speed, and then I talk about the next merits. I also introduce my open source library, and I always think about how to write a title, but I haven't been able to find something clear other than this speed yet. If you have any good ideas, please let me know.

Collapse
 
gamedevsam profile image
Samuel Batista

Very interesting library, I'd like to read some more articles describing how the speed-ups are achieved and what are the downsides of your approach as opposed to alternatives (also an explanation why "ts-patch" is required).

I'd also like to see a benchmark package, so your performance claims can be reproduced by myself and others so we can make an informed decision before adopting the library in production.

Collapse
 
samchon profile image
Jeongho Nam
Collapse
 
devgancode profile image
Ganesh Patil

Informative article
Learning nextjs now this may be help me in next project.

Thankyou....!!

Collapse
 
realtrynna profile image
realtrynna

Holy Molly Shit is so amazing.

Collapse
 
glen profile image
치와와

what the... is so amazing👍

Collapse
 
jmcelreavey profile image
John McElreavey

Is there a full stack mono repo example of this? Would love to see one!

Collapse
 
galwa profile image
Gal

What about async validation ? cannt find referance

Collapse
 
samchon profile image
Jeongho Nam

Why asynchronous validation is required in DTO level?

Can you give me an exmaple case?

Collapse
 
galwa profile image
Gal

Any use case were it doesnt make sense to seperate the buissness logic validation from the DTO itself for rexample You have DTO A that Point to DTO B and yuwant validate the B exists

Collapse
 
vandres profile image
Volker Andres

Replacing decorators with JSDoc feels like going 10 steps back.

Collapse
 
samchon profile image
Jeongho Nam

Well, if you think below code seems advanced, nothing to say:

export class BbsArticle {
    @IsString()
    @ApiProperty({
        format: "uuid",
    })
    id!: string;

    // DUPLICATED SCHEMA DEFINITION
    // - duplicated function call + property type
    // - have to specify `isArray` and `nullable` props by yourself
    @IsArray()
    @IsObject()
    @ValidateNested()
    @Type(() => AttachmentFile)
    @ApiProperty({
        type: () => AttachmentFile,
        nullable: true,
        isArray: true,
        description: "List of attached files.",
    })
    files!: AttachmentFile[] | null;

    @IsString()
    @IsOptional()
    @ApiProperty({
        type: "string",
        nullable: true,
        minLength: 5,
        maxLength: 100,
        description: "Title of the article.",
    })
    title!: string | null;

    @IsString()
    @ApiProperty({
        description: "Main content body of the article."
    })
    body!: string;

    @IsString()
    @ApiProperty({
        format: "date-time",
        description: "Creation time of article",
    })
    created_at!: string;
}

export class AttachmentFile {
    @IsString()
    @IsOptional()
    @ApiProperty({
        type: "string",
        nullable: true,
        maxLength: 255,
        pattern: "^[a-zA-Z0-9-_]+$",
        description: "File name.",
    })
    name!: string | null;

    @IsString()
    @IsOptional()
    @ApiProperty({
        type: "string",
        nullable: true,
        maxLength: 255,
        pattern: "^[a-zA-Z0-9-_]+$",
        description: "File extension.",
    })
    extension!: string | null;

    @IsString()
    @ApiProperty({
        format: "url",
        description: "URL of the file.",
    })
    url!: string;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
zimiovid profile image
zimiovid • Edited

Do you have any plans integrate nestjs with Graphql nest implementation?

Collapse
 
samchon profile image
Jeongho Nam

As I've not used it yet, have no insight about it.

Can you tell me which points of gql in NestJS are inconvenient, so that how feature do you want in nestia, as an issue?