NestJS综述目录
第一步
在这组文章中,您将了解Nest的核心基础知识。 为了熟悉Nest应用程序的基本构建块,我们将构建一个基本的CRUD应用程序,其功能涵盖介绍性的大量基础知识。
语言
我们热爱TypeScript,但最重要的是,我们热爱Node.js。这就是Nest同时兼容TypeScript和纯JavaScript的原因。 Nest利用了最新的语法特性,因此要将其用于纯JavaScript,我们需要一个Babel编译器。
我们将在提供的示例中主要使用TypeScript,但您始终可以将代码片段切换为普通JavaScript语法(只需单击每个片段右上角的语言按钮即可切换)
先决条件
请确保您的操作系统上安装了Node.js(版本>=16)
设置
使用 Nest CLI设置新项目非常简单。安装npm后,您可以再操作系统终端中使用以下命令创建一个新的Nest项目:
npm -i -g @nestjs/cli
nest new project-name
如果要使用TypeScript更严格的功能集创建新项目,请使用--strict标志传递给nest new命令。
将创建project-name目录,安装节点模块和一些其它样板文件,并将创建src/目录并填充几个核心文件。
src
|--app.controller.spec.ts
|--app.controller.ts
|--app.module.ts
|--app.service.ts
|--main.ts
以下是这些核心文件的简要概述:
| app.controller.ts | 具有单一路线的基本控制器 |
|---|---|
| app.controller.spec.ts | 控制器的单元测试 |
| app.module.ts | 应用程序的根模块 |
| app.service.ts | 具有单一方法的基础服务 |
| main.ts | 应用程序的入口文件,使用核心函数NestFactory创建Nest应用程序实例 |
main.ts包含一个异步函数,它将引导我们的应用程序。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
要创建Nest应用程序实例,我们需要使用核心NestFactory类。
NestFactory类提供了几个静态方法,用于创建应用程序实例。
create()方法会返回一个应用程序对象,该对象符合INestApplication接口。
该对象提供了一系列方法,这些方法将在接下来的章节中介绍。在上面的main.ts示例中,
我们只需启动HTTP监听器,让应用程序等待入站HTTP请求即可。
请注意,使用Nest CLI搭建的项目会创建一个初始化项目结构,鼓励开发人员遵循每个模块 保留在其自己的专用目录中的约定。
默认情况下,如果在创建应用程序时发生任何错误,
您的应用程序将退出并显示代码1。
如果您想让它抛出错误,请禁用abortOnError选项,例如
NestFactory.create(AppModule, {abortOnError: false})
平台
Nest的目标是成为一个与平台无关的框架。 平台独立性使得创建可重复使用的逻辑部件成为可能,开发人员可以再多种不同类型 的应用程序中利用这些逻辑部件。 从技术上讲,一旦创建了适配器,Nest就能与任何Node HTTP框架协同工作。 开箱即支持两种HTTP平台:express和fastify。您可以选择最适合您需求的一种。
-
express平台
Express是一个著名的极简网络节点框架。 它是一个经过实战检验、可用于生产的库,拥有大量由社区提供的资源。 默认使用
@nestjs/platform-express包。 许多用户都能很好地使用Express,无需采取任何措施启用它。 -
fastify平台
Fastify是一个高性能、低开销的框架,高度专注于提供最大的效率和速度。 在这里阅读如何使用它
无论使用哪个平台,它都会公开自己的应用程序结构,它们分别被视为NestExpressApplication
和NestFastifyApplication。
如下例所示,当你向NestFactory.create()方法传递一个类型时,
应用程序对象将拥有该特定平台专用的方法。但请注意,除非您真的想访问底层平台API,否则无需指定类型。
const app = await NestFactory.create<NestExpressApplication>(AppModule);
运行应用程序
安装过程完成后,您可以再操作系统命令提示符下运行以下命令来启动应用程序侦听入站HTTP请求:
npm run start
为了加快开发过程(构建速度加快20倍),您可以通过将-b swc标志传递给启动脚本来使用SWC构建器,
如下所示npm run start -- -b swc
此命令将启动应用程序,HTTP服务器将监听src/main.ts文件中定义的端口。
应用程序运行后,打开浏览器并导航至http://localhost:3000。您将看到Hello World!!
要查看文件中的更改,可以运行以下命令启动应用程序:
npm run start:dev
该命令将监听您的文件,自动重新编译并重新加载服务器。
语法检查和格式化
CLI尽最大努力构建可靠大规模开发工作流程。因此,生成的Nest项目预装了代码linter和
formatter程序(分别为eslint和prettier)
不确定格式化程序与代码检查的作用?在这里了解差异
为了确保最大的稳定性和可扩展性,我们使用基本的eslint和prettier包。
此设置允许IDE在设计上与官方扩展完美集成。
# 使用eslint进行lint和自动修复
npm run lint
# 使用prettier设置的格式进行代码格式化
npm run format
对于IDE不相关的无头环境(持续集成、Git挂钩等),Nest项目附带了现成的npm脚本
控制器
控制器负责处理传入请求并向客户端返回响应。
控制器的作用是接收应用程序的特定请求。 路由机制控制哪个控制器接收哪些请求。 通常情况下,每个控制器都有不止一个路由,不同的路由可以指定不同的操作。
为了创建基本控制器,我们使用类和装饰器。 装饰器将类与所需的元数据关联起来,并使Nest能够创建路由映射(将请求绑定到相应的控制器)。
为了快速创建带有内置验证的CRUD控制器,您可以使用CLI的CRUD生成器:nest g resource [name]
路由
在下面的示例中,我们将使用@Controller()装饰器,这是定义基本控制器所必需的。
我们将指定一个可选的路径前缀:cats。在@Controller()装饰器中使用路径前缀
可以让我们轻松地将一组相关的路由分组,并最大限度地减少重复代码。
例如,我们可以选择将一组管理与猫实体交互的路由归类到路由/cats下。
在这种情况下,我们可以再@Controller()装饰器中指定路径前缀cats,
这样就不必为文件中的每个路由重复写路径前缀的这一部分。
import { Controller, Get } from '@nestjs/commom';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
要使用CLI创建控制器,只需执行nest g controller [name]命令
findAll()方法之前的@Get() HTTP请求方法装饰器告诉Nest为HTTP请求的特定端点创建处理程序。
端点对应于HTTP请求方法(在本例中为GET)和路由路径。路由路径是什么?
处理程序的路由路径是通过连接为控制器声明的(可选)前缀和方法装饰器中指定的任何路径来确定的。
由于我们已经为每个路由(cats)声明了一个前缀,并且没有在装饰器中添加任何路径信息,
因此Nest会将GET/cats请求映射到此处理程序。
如前所述,路径包括可选的控制器路径前缀和请求方法装饰器中声明的任何路径字符串。
例如,cats的路径前缀与装饰器@Get('breed')结合将为GET /cats/breed等请求生成路由映射。
在上面的示例中,当前此端点发出GET请求时,Nest将请求路由到我们用户定义的findAll()方法。
请注意,我们在这里选择的方法名称是完全任意的。显然,我们必须声明一个方法来绑定路由,
但Nest并不赋予所选方法名称任何意义。
此方法将返回200状态码和关联的响应,在本例中只是一个字符串。为什么会发生这种情况? 为了解释这一点,我们首先介绍Nest使用两种不同选项来操纵响应的概念:
-
标准(推荐)
使用此内置方法,当请求处理程序返回 JavaScript 对象或数组时,它将自动序列化为 JSON。 然而,当它返回 JavaScript 基本类型(例如字符串、数字、布尔值)时,Nest 将仅发送该值,而不尝试对其进行序列化。 这使得响应处理变得简单: 只需返回值,Nest 就会处理其余的事情。
此外,默认情况下,响应的状态码始终为200,但使用 201 的 POST 请求除外。 我们可以通过在处理程序级别添加
@HttpCode(...)装饰器来轻松更改此行为(请参阅状态代码)。 -
特定库
我们可以使用特定于库的(例如 Express)响应对象,可以使用方法处理程序签名中的
@Res()装饰器注入该对象(例如findAll(@Res() response))。 通过这种方法,您可以使用该对象公开的本机响应处理方法。例如,使用Express,您可以使用像response.status(200).send()这样的代码构建响应。
Nest 检测处理程序何时使用@Res()或@Next(),表明您已选择特定库的选项。
如果同时使用两种方法,则该单一路线的标准方法将自动禁用,并且将不再按预期工作。
要同时使用这两种方法(例如,通过注入响应对象以仅设置 cookie/headers,但仍将其余部分留给框架),
您必须在@Res({ passthrough: true })装饰器设置passthrough为true。
请求对象
处理程序通常需要访问客户端请求的详细信息。Nest 提供对底层平台(默认为 Express)请求对象的访问。 我们可以在处理程序的签名中添加 @Req() 装饰器,指示 Nest 注入请求对象,从而访问请求对象
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
return 'This action returns all cats';
}
}
为了利用express的类型(如上面的request: Request请求参数示例),请安装@types/express包
request对象代表HTTP请求,具有请求查询字符串、参数、HTTP头信息和正文的属性。
在大多数情况下,无需手动抓取这些属性,我们可以使用专有的装饰器,例如@Body()或@Query(),
这些装饰器开箱即用。下面列出所提供的装饰器以及它们所代表的特定平台对象。
@Request(), @Req() | req |
|---|---|
@Response(), @Res() * | res |
@Next() | next |
@Session() | req.session |
@Param(key?: string) | req.params / req.params[key] |
@Body(key?: string) | req.body / req.body[key] |
@Query(key?: string) | req.query / req.query[key] |
@Headers(name?: string) | req.headers / req.headers[name] |
@Ip() | req.ip |
@HostParam() | req.hosts |
*为了与底层HTTP平台(例如Express和Fastify)之间的类型兼容,Nest提供了@Res和@Response()装饰器。
@Res()只是@Response()的别名。两者都直接公开底层平台响应对象接口。
使用它们时,您还应该导入底层库的类型(例如@types/express)以充分利用他们。
请注意,当您在方法处理程序中注入@Res()或@Response()时,您会将Nest置于该处理程序的特定库的模式,
并且您将负责管理响应。
执行此操作时,您必须通过调用响应对象来发出某种类型的响应,否则HTTP服务器将挂起。
例如(res.json(...)或res.send(...))
资源
之前,我们定义了一个端点来获取cats资源(GET路由)。我们还希望提供一个创建新纪录的端点。 为此,我们创建POST处理程序。
import { Controller, Get, Post } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Post()
create(): string {
return 'This action adds a new cat';
}
@Get
findAll(): string {
return 'This action returns all cats';
}
}
就是这么简单。Nest为所有标准HTTP方法提供了装饰器:
@Get@Post@Put@Delete()@Patch()@Options()@Head()@All()其中All()定义了一个处理所有这些端点。
路由通配符
Nest还支持基于模式的路由。例如星号(asterisk)用作通配符,将匹配任意字符组合。
@Get('ab*cd')
findAll() {
return 'This route uses a wildcard';
}
ab*cd路由路径将匹配abcd,ab_cd,abecd。
字符?、+、*