Nest.js 中的 TypeSafe 方案
在现代 Web 开发中,类型安全(TypeSafe)是提升代码质量和减少运行时错误的关键因素。
Nest.js 作为一个渐进式的 Node.js 框架,结合了 TypeScript 的强大功能,提供了构建高效、可扩展服务器端应用的理想平台。
笔者在使用 Nest.js 构建全栈应用时最大的痛点是写了这么多类型检查,好像没有办法和前端通用啊~。相信许多人也有这个问题,所以也冒出了在 Nest.js 中集成 tRPC 的教程。
而本文介绍的 Nestia,一个专为 Nest.js 设计的类型安全解决方案,帮助开发者在构建应用时实现更高的类型安全性和开发效率。
韩国最好的程序员
Jeongho Nam ,GitHub用户名为 samchon,他在 README 中自称为 韩国最好的程序员。他自1998年开始编程,拥有25年的丰富经验。在这段时间里,他开发了许多程序,并不断提升自己的技能。他不仅在工作中开发程序,还在业余时间开发开源项目,以满足自身需求或改进现有功能。这些开源项目逐渐形成了新的开源库,其中最著名的就是 typia 和 nestia 。
什么是Nestia?
Nestia 是一个专为 Nest.js 开发的库,旨在通过利用 TypeScript 的类型系统,提供更高效和类型安全的开发体验。Nestia 的核心目标是简化数据传输对象(DTOs)的定义和验证,减少类型错误,并提升代码的可维护性。
Nestia的主要功能
-
类型安全的 DTO 定义和验证:
Nestia 利用 TypeScript 的类型系统,允许开发者定义类型安全的 DTOs。通过自动生成的类型定义,Nestia 确保了数据在传输和处理过程中的一致性,避免了常见的类型错误。
NestJS需要三个重复的 DTO 模式定义。第一个是定义 TypeScript 类型,第二个和第三个是调用
class-validator
和@nestjs/swagger
的装饰器函数。这不仅繁琐,而且容易出错。如果你在第 2 或第 3 处写错了的话,TypeScript 编译器是无法检测到的。只有在运行时才能检测到。换句话说,它并不是类型安全的。 -
自动生成API客户端:
Nestia 可以根据服务器端的 API 定义,自动生成类型安全的 API 客户端。这种方式不仅减少了手动编写客户端代码的工作量,还确保了前后端的一致性。
这一功能与著名的 tRPC 库有相似之处。tRPC 是一个端到端类型安全的 RPC 框架,它允许你轻松构建和使用完全类型安全的 API,无需模式或代码生成。tRPC 的主要作用是在全栈 TypeScript 项目中提供类型安全的 API 调用,大大提高了开发效率和代码质量。但 tRPC 的问题是通常要求前后端代码位于同一个 monorepo 中 ,以便共享类型定义。这种紧耦合的架构可能不适合所有项目,特别是那些前后端分离开发或需要为第三方提供 API 的场景。相比之下,Nestia 通过自动生成独立的 API 客户端解决了这个问题。它允许你在保持类型安全的同时,将生成的 SDK 作为独立的包分发给客户端开发者。这种方式既保留了类型安全的优势,又提供了更大的灵活性,使得 Nestia 在更广泛的项目结构和开发场景中都能发挥作用。
-
高效的JSON序列化和反序列化:
Nestia 提供了高效的 JSON 序列化和反序列化功能,利用 TypeScript 的类型信息,显著提升了性能和类型安全性。
如何使用Nestia
安装Nestia
你可以运行以下命令通过模版代码来快速上手 Nestia。 模板将自动构建在<directory>
中。作为参考,这是一个最小的模板项目,只集中于从 NestJS 服务器生成 SDK。它不包含数据库连接。
bash
npx nestia start <directory>
你也可以运行下面的命令来将 Nestia 集成至现有的项目中。设置向导将自动安装并配置所有内容。
bash
npx nestia setup
定义类型安全的DTO
你不需要掌握特殊的语法只使用 TypeScript 就可以编写一个带有类型检查的 DTO。当然,Nestia 也通过 Typia 提供了编写复杂类型检查的可能,例如,我们可以定义一个论坛完整的 DTO:
typescript
import { tags } from "typia";
export interface IBbsArticle {
/* Primary Key. */
id: string & tags.Format<"uuid">;
/* Title of the article. */
title: null | (string & tags.MinLength<5> & tags.MaxLength<100>);
/* Main content body of the article. */
body: string;
/* Creation time of article. */
created_at: string & tags.Format<"date-time">;
}
在 Controller
中调用 Nestia 的装饰器
NestJS 原生的装饰器(如 @Get()
, @Post()
等)虽然使用方便,但在性能和类型安全方面存在一些局限:
- 使用 class-validator 和 class-transformer 进行验证和转换,性能相对较低
- 需要定义额外的 DTO 类和装饰器,增加了代码量
- 类型安全性不够强,运行时可能出现类型错误
Nestia 的装饰器(如 @TypedRoute.Get()
, @TypedBody()
等)则解决了这些问题:
- 利用 typia 库进行高性能的运行时类型验证,比 class-validator 快 20,000 倍
- 支持使用纯 TypeScript 接口定义 DTO,无需额外的类定义
- 在编译时进行类型检查,提供更强的类型安全性
- JSON 序列化速度比 class-transformer 快 200 倍
typescript
import { TypedRoute } from "@nestia/core";
import { Controller } from "@nestjs/common";
import { IBbsArticle } from "./IBbsArticle";
@Controller("bbs/articles")
export class BbsArticlesController {
@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: [],
};
}
@TypedRoute.Post()
public async store(
@TypedBody() input: IBbsArticle.IStore,
): Promise<IBbsArticle> {
return {
...input,
id: "2b5e21d8-0e44-4482-bd3e-4540dee7f3d6",
created_at: "2023-04-23T12:04:54.168Z",
};
}
}
自动生成API客户端
Nestia可以根据服务器端的API定义,自动生成类型安全的API客户端。在根目录配置 nestia.config.ts
文件
typescript
import { INestiaConfig } from '@nestia/sdk';
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter } from '@nestjs/platform-fastify';
import { AppModule } from './src/app.module';
const NESTIA_CONFIG: INestiaConfig = {
input: async () => {
const app = await NestFactory.create(AppModule, new FastifyAdapter());
app.setGlobalPrefix('api');
return app;
},
output: 'src/api',
clone: true,
distribute: 'sdk',
};
export default NESTIA_CONFIG;
运行命令 npx nestia sdk
即可生成 SDK package,你可以直接在 Monorepo 中使用它,也可以将其分发到 npm.
尝试使用生成的 SDK 看到类型提示的那一刻,整个人都通畅了~
结论
Nestia 作为一个专为 Nest.js 设计的类型安全解决方案,通过简化 DTO 定义和验证、自动生成 API 客户端以及高效的 JSON 序列化和反序列化功能,帮助开发者在构建应用时实现更高的类型安全性和开发效率。无论是大型企业级应用还是个人项目,Nestia 都是提升代码质量和开发效率的理想选择。
通过本文的介绍,希望您对 Nestia 有了更深入的了解,并能在您的 Nest.js 项目中尝试使用这一强大的工具,享受类型安全带来的诸多好处。