I made Express faster than Fastify (100x faster JSON, also NestJS)
Tags: javascript, typescript, opensource, programming
Published: 2023-04-03 13:09:55

## Outline
Hello, I am developer of [typia](https://github.com/samchon/typia), and studying `fastify` in nowadays. 

During the study, I could understand why `fastify` is faster than `express`. Also, I did an experiment imitating the secret of the `fastify` in `express` with [typia's faster JSON stringify function](https://dev.to/samchon/i-made-10x-faster-jsonstringify-functions-even-type-safe-2eme). 

From the experiment (benchmark), I could get interesting result that `express` became faster than `fastify`. So I would like to share it with you.

![JSON stringify benchmark in server level](https://github.com/samchon/typia/raw/master/benchmark/results/AMD%20Ryzen%207%206800HS%20with%20Radeon%20Graphics/images/stringify_po_server_pc.svg)

> Made `express` to be faster than `fastify`

-------------

## What [typia](https://github.com/samchon/typia) is
```typescript
// RUNTIME VALIDATORS
export function is<T>(input: unknown | T): input is T; // returns boolean
export function assert<T>(input: unknown | T): T; // throws TypeGuardError
export function validate<T>(input: unknown | T): IValidation<T>; // detailed
export const customValidators: CustomValidatorMap; // can add custom validators

// STRICT VALIDATORS
export function equals<T>(input: unknown | T): input is T;
export function assertEquals<T>(input: unknown | T): T;
export function validateEquals<T>(input: unknown | T): IValidation<T>;

// JSON
export function application<T>(): IJsonApplication; // JSON schema
export function assertParse<T>(input: string): T; // type safe parser
export function assertStringify<T>(input: T): string; // safe and faster
    // +) isParse, validateParse 
    // +) stringify, isStringify, validateStringify

// MISC
export function random<T>(): Primitive<T>; // generate random data
export function clone<T>(input: T): Primitive<T>; // deep clone
export function prune<T extends object>(input: T): void; // erase extra props
    // +) isClone, assertClone, validateClone
    // +) isPrune, assertPrune, validatePrune
```

Before telling detailed stories, I'll introduce [typia](https://github.com/samchon/typia) for a while. 

It is a runtime validator library for TypeScript, which can perform above features just by only one line, just by utilizing pure TypeScript type. On the other hand, all of other alternative libraries require extra and duplicated schema definitions, which are different with the TypeScript type.

Furthermore, validation speed of [typia](https://github.com/samchon/typia) is much faster than others. Comparing validation speed, [typia](https://github.com/samchon/typia) is maximum 15,000x faster than `class-validator`. When it comes to the JSON stringify function, [typia](https://github.com/samchon/typia) is maximum 100x faster than `class-transformer` and even type safe.

-------------

## Secret of `fastify`
[fastify](https://www.fastify.io/) is a competitive library of `express`, which uses faster speed as a weapon. 

And one of the reason why `fastify` is faster than `express` is, [fast-json-stringify](https://github.com/fastify/fast-json-stringify). `fast-json-stringify` is another library what `fastify` team had developed, which boosts up JSON conversion speed by analyzing JSON schema definition. 

By using the `fast-json-stringify` library, `fastify` can serialize JSON string much faster than `express`, and such difference makes `fastify` to be faster than `express`.

```js
const fastJson = require('fast-json-stringify')

// REQUIRES JSON SCHEMA DEFINITION
const stringify = fastJson({
    title: 'Example Schema',
    type: 'object',
    properties: {
        firstName: {
            type: 'string'
        },
        lastName: {
            type: 'string'
        },
        age: {
            description: 'Age in years',
            type: 'integer'
        },
        reg: {
            type: 'string'
        }
    }
});

// MAKES JSON SERIALIZATION FASTER
console.log(stringify({
    firstName: 'Matteo',
    lastName: 'Collina',
    age: 32,
    reg: /"([^"]|\\")*"/
}));
```

![Stringify Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/stringify.svg)

> `fast-json-stringify` is faster than native `JSON.stringify()` function

-------------

## Imitate secret of `fastify` in `express`
```typescript
import typia from "typia";

// PURE TYPESCRIPT TYPE
interface IPerson {
    firstName: string;
    lastName: string;
    age: number; // Age in years
    reg: RegExp;
}

// EASIER THAN ANY OTHER LIBRARIES
typia.stringify<IPerson>({
    firstName: 'Matteo',
    lastName: 'Collina',
    age: 32,
    reg: /"([^"]|\\")*"/
});
```

Studying source code of `fastify`, I could understand why `fastify` is faster. 

By the way, [typia](https://github.com/samchon/typia) has the same function like `fast-json-stringify`. Therefore, imitating secret of `fastify` was easily possible, too.

```typescript
//----
// EXPRESS + TYPIA
//----
import express from "express";
import typia from "typia";

const server: express.Express = express();
const reply =
    <T>(stringify: (input: T) => string | null) =>
    (data: T) =>
    (_req: express.Request, res: express.Response) =>
        res
            .status(200)
            .header("Content-Type", "application/json")
            .send(stringify(data));

// VERY EASY TO IMPLEMENT
server.get(
    "/ObjectSimple",
    reply(typia.createIsStringify<ObjectSimple[]>())
         (storage.ObjectSimple),
);
```

Here is the code imitating `fastify` library in `express` with [typia](https://github.com/samchon/typia).

I think that my solution is much easier than `fastify`, because [typia](https://github.com/samchon/typia) does not require complicate JSON schema definition, and it just requires only pure TypeScript type. 

Do you agree?

```typescript
//----
// FASTIFY
//----
import fastify, { FastifyReply, FastifyRequest } from "fastify";
import typia from "typia";

const server = fastify();
const schema = (app: typia.IJsonApplication) => {
    const definitions: Record<string, typia.IJsonSchema> = {};
    for (const [key, value] of Object.entries(app.components.schemas))
        definitions[key.replace("#/definitions/", "")] = value;

    return {
        schema: {
            response: {
                200: {
                    ...app.schemas[0]!,
                    definitions,
                },
            },
        },
    };
};
const reply = (data: object) => (_i: FastifyRequest, o: FastifyReply) =>
    o.send(data);

// DEFINING JSON SCHEMA IS A TERRIBLE WORK
// THEREFORE, I JUST USED typia.application() FUNCTION
server.get(
    "/ObjectSimple",
    schema(typia.application<[ObjectSimple[]], "ajv", "#/definitions">()),
    reply(storage.ObjectSimple),
);
```

Also, to proceed experiment (benchmark) in the `fastify` side, I wrote server code of `fastify`, too. By the way, as defining JSON schema is a terrible work for me, I just generated the JSON schema data through `typia.application()` function.

Measuring benchmark of those servers through [autocannon](https://github.com/mcollina/autocannon) (another library what `fastify` team had built), I could get awesome result. `express` became faster than `fastify` in some cases, just by utilizing `typia.stringify()` function.

![JSON stringify benchmark on server](https://github.com/samchon/typia/raw/master/benchmark/results/AMD%20Ryzen%207%206800HS%20with%20Radeon%20Graphics/images/stringify_po_server_pc.svg)

> `express` became faster than `fastify` in some cases

-------------

## Boost up your NestJS server speed
![Validation Benchmark](https://github.com/samchon/typia/raw/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz/images/is.svg)

> `class-validator` and `class-transformer` are extremely slow

You know what? NestJS is utilizing `class-validator` and `class-transformer`. Do you remember? those libraries were slowest than any other libraries in the above benchmarks.

  - `class-validator` is 15,000x times slower than [typia](https://github.com/samchon/typia)
  - `class-transformer` is 100x times slower than [typia](https://github.com/samchon/typia)

If you replace them to [nestia](https://github.com/samchon/nestia) (wrappers of [typia](https://github.com/samchon/typia) for NestJS) supported decorators, you can boost up your NestJS developed backend server speed. Just by replacing some decorator functions like below, your server program would be much faster.

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

import type { IBbsArticle } from "@bbs-api/structures/IBbsArticle";

@Controller("bbs/articles")
export class BbsArticlesController {
    /** 
     * Store a new content.
     * 
     * @param inupt Content to store
     * @returns Newly archived article
     */
    @TypedRoute.Post() // 100x faster and safer JSON.stringify()
    public async store(
        // 15,000x faster validator
        @TypedBody() input: IBbsArticle.IStore 
    ): Promise<IBbsArticle>; 
        // do not need DTO class definition, 
        // just fine with interface
}
```

Also, with the [nestia](https://github.com/samchon/nestia), you can build evolved swagger than swagger. Furthermore, you can build SDK library (like tRPC), therefore, client deevelopers can use your API much easily and safely like below.

![SDK utilization code](https://user-images.githubusercontent.com/13158709/215004990-368c589d-7101-404e-b81b-fbc936382f05.gif)

> Frontend developers would be happy

-------------

## `fastify` is still fast

Looking back above benchmark result, `fastify` is faster than combination of `express` and `typia`, when response data is small. If making the reponse data much smaller, `fastify` becomes always faster than `express` + `typia`.

I just assume that `fastify` has special optimized logic for header parsing, and reducing waiting time for each API call, but I don't know the exactly reason why. I need to study `fastify` more, and also need to study `express`, too.

I will `fastify` continuously, and will come back again with the reason why.

Thanks for reading my article, and hope my article was enjoyable.

-------------

## Links
  - Github Repositories
    - samchon
      - [typia](https://github.com/samchon/typia)
      - [nestia](https://github.com/samchon/nestia)
    - fastify
      - [fastify](https://github.com/fastify/fastify)
      - [fast-json-stringify](https://github.com/fastify/fast-json-stringify)
      - [autocannon](https://github.com/mcollina/autocannon)
    - typestack
      - [class-validator](https://github.com/typestack/class-validator)
      - [class-transformer](https://github.com/typestack/class-transformer)
  - Benchmark Program
    - [fastify](https://github.com/samchon/typia/blob/master/benchmark/servers/stringify/fastify.ts)
    - [express + typia](https://github.com/samchon/typia/blob/master/benchmark/servers/stringify/express_is_stringify.ts)
    - [used data](https://github.com/samchon/typia/tree/master/test/structures)
  - Benchmark Results
    - [Intel i5-1135G7](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz)
    - [AMD Ryzen 7 6800HS](https://github.com/samchon/typia/tree/master/benchmark/results/AMD%20Ryzen%207%206800HS%20with%20Radeon%20Graphics)