Nest探究(一):初识Nest.js

543 阅读9分钟

前言

作为一名前端开发者,虽然还是菜鸟级别,但是作为一名有着优秀的开发人员的思想,还是要多学习其他的技术的,所谓技多不压身。这不是在后端的路上慢慢探究,前有node.js 的koa、express,这不是最近又出现了nest.js,手比较痒,就替大伙先尝试尝试,官网在此,同学们可前去查看:Nest.js官方文档

Nest.js介绍

Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架。它利用JavaScript 的渐进增强的能力,使用并完全支持 TypeScript (仍然允许开发者使用纯 JavaScript 进行开发),并结合了 OOP (面向对象编程)、FP (函数式编程)和 FRP (函数响应式编程)。

在底层,Nest 构建在强大的 HTTP 服务器框架上,例如 Express (默认),并且还可以通过配置从而使用 Fastify !

Nest 在这些常见的 Node.js 框架 (Express/Fastify) 之上提高了一个抽象级别,但仍然向开发者直接暴露了底层框架的 API。这使得开发者可以自由地使用适用于底层平台的无数的第三方模块。

Nest.js项目创建

// 全局安装Nest
npm i -g @nestjs/cli  
// 创建项目
nest new project-name  

创建项目成功之后,会让你选择依赖包管理工具,这里我选择更快的yarn,如果你有安装yarn,最好也选择yarn,能更快一些,npm在国内安装速度会慢一些。

注意: Nest.js 要求 Node.js(>= 10.13.0,v13 除外), 如果你的Node.js 版本不满足要求,可以通过nvm包管理工具安装符合要求的Node.js版本

图片.png

之后等待安装

图片.png

安装完之后可以按照其提示,运行项目

目录

打开文件之后,就是这样一个目录

图片.png

src还是一成不变,作为其核心文件,我简要说一下:

文件描述
app.controller.ts单个路由的基本控制器(Controller)
app.controller.spec.ts针对控制器的单元测试
app.module.ts应用程序的根模块(Module)
app.service.ts具有单一方法的基本服务(Service)
main.ts应用程序的入口文件,它使用核心函数NestFactory来创建 Nest 应用程序的实例。

Nest.js剖析

package.json

我们去查看package.json的文件配置,其中两个关乎两个项目启动的最常用的命令行

图片.png

-   `yarn start`:运行项目(未启用热更新)
-   `yarn start:dev`:运行项目(启用热更新)

个人强烈建议使用热更新的命令,如果嫌命令行麻烦,可以自己手动更改项目启动的命令行,二者合并也是可以的,我就为了方便将其合并为yarn run start就可以启动热更新了。

图片.png

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.js的工厂函数NestFactory来创建了一个AppModule实例,之后启动了 HTTP 侦听器,侦听main.ts 中所定义的3000端口。 此处的端口如若被占用,可以改为其他的端口号,之后我们在前端页面访问

熟悉的Hello Word

图片.png

app.module.ts

那么Hello Word是怎么来的呢?我们看到此处引入了当前文件夹下面的app.module,查看代码

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

看到该文件的时候,是不是有的人就要惊呼了,我靠,这不是Java里的springBoot吗?反正我是呆了一下,这可不就是吗?继续看下去,学过的会更加震惊。

.mudule文件需要使用了一个@Module() 装饰器的类,装饰器可以理解成一个封装好的函数,@Module() 装饰器接收四个属性:providerscontrollersimportsexports

  • providers:Nest.js注入器实例化的提供者(服务提供者),处理具体的业务逻辑,各个模块之间可以共享
  • controllers:处理http请求,包括路由控制,向客户端返回响应,将具体业务逻辑委托给providers处理;
  • imports:导入模块的列表,如果需要使用其他模块的服务,需要通过这里导入;
  • exports:导出服务的列表,供其他模块导入使用。如果希望当前模块下的服务可以被其他模块共享,需要在这里配置导出;

这里呢,在我看来,providers就是提供者,也就说谁支撑起这个模块,能让这模块得以使用,也就是所谓的方法函数;控制器更是如其名,控制这个模块的路由,更像是一个中间轴,而imports和exports相信前端的各位对其也是容易理解,就是引入其他模块供自己使用或者抛出自己的模块供其他模块使用

app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

使用@Controller装饰器来定义控制器, 在其中定义AppController类并抛出,将引入进来的AppService作为构造器的私有只读属性,@Get是请求方法的装饰器,对getHello方法进行修饰,表示这个方法会被GET请求调用,而该方法调用了AppService下的 getHello方法

每一个要成为控制器的类,都需要借助@Controller装饰器的装饰,该装饰器可以传入一个路径参数,作为访问这个控制器的主路径,比如@Controller('home'),那么该路径就可以通过本地localhost:3000拼接home来访问,同样在接口当中可以定义其他的路由后缀。比如如果定义 @Get('home),那么该路径也可以通过本地localhost:3000拼接home来访问

在更改@Get里面的内容后,本地3000端口变成了404

图片.png

加上后缀之后才访问正常

图片.png

并且该@Get装饰器还可以替换为@Post @Put 等装饰器,需要前端用相同的方法访问。

全局路由前缀

在公司中,一般都有api为开头的接口,那在每个controller.ts文件加这个前缀就太麻烦了,可以选择在main.ts文件中使用setGlobalPrefix设置全局路由前缀

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 设置全局路由前缀
  app.setGlobalPrefix('api');
  await app.listen(3000);
}
bootstrap();

app.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

@Injectable修饰后的 AppService,抛出该类, 在AppModule中注册之后,在app.controller.ts中使用,我们就不需要使用new AppService()去实例化,直接引入过来就可以使用里面定义的方法。

app.service.spec.ts

这个后缀为spec的文件是测试文件,在快速开发的情况下,比较少用到这个文件,如果不需要每次都生成这个文件,可以在nest-cli.json文件中添加如下配置,禁用测试用例生成

"generateOptions": { "spec": false }

Nest简便命令行

在编写代码前,先说几个命令行

图片.png

我们就使用该命令行来创建

//语法
nest g [文件类型] [文件名] [文件目录]
nest g mo xxx    创建模块
nest g co xxx    创建控制器
nest g service xxx   创建服务类
  • 注意创建顺序: 先创建Module, 再创建ControllerService, 这样创建出来的文件在Module中自动注册,如果后创建Module, ControllerService,会被注册到外层的app.module.ts,所以创建顺序需要注意一下。

  • 如果忘记命令可以使用nest --help进行查看

图片.png

当然还有一个很好用的命令行,三个文件给你一块创建,省时省力,你值得拥有

nest g resource user 
使用简写 nest g res user

图片.png

  • 在此选择REST API风格

  • nest g res user就是创建完整的user模块,这个命令执行完后会生成一个user文件夹,在这个文件夹中包含如下图所示的文件

    • dto用于定义系统内的接口或者输入和输出
    • entitiesmysql数据库中的一张表的实体文件,但要在文件中使用@Entity装饰器,这个在下文中介绍数据库连接、增删改查时会使用到
    • 这个后缀为spec的文件是测试文件,在快速开发的情况下,比较少用到这个文件,如果不需要每次都生成这个文件,可以在nest-cli.json文件中添加如下配置,禁用测试用例生成."generateOptions": { "spec": false }

这里第二个问题是与数据库相关的,此处不过多解释,默认yes,多多益善嘛

图片.png

创建成功之后,目录结构就改变了

图片.png

看完了里面的文件之后,好家伙,增删改查都给你写完了,真省事!老铁直呼666!真贴心啊

//user.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UserService {
  create(createUserDto: CreateUserDto) {
    return 'This action adds a new user';
  }
  findAll() {
    return `This action returns all user`;
  }
  findOne(id: number) {
    return `This action returns a #${id} user`;
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }

  remove(id: number) {
    return `This action removes a #${id} user`;
  }
}

swagger

后端开发,必不可少的就是开发文档了,这nest不是我说,是真滴贴心,贼方便,早早给你想好了。

安装

npm install --save @nestjs/swagger

接下来需要在main.ts中设置Swagger文档信息:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  // 设置swagger文档
  const config = new DocumentBuilder()
    .setTitle('swagger文档')  
    .setDescription('接口文档')
    .setVersion('1.0')
    .addBearerAuth()
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('/api/swagger', app, document);
  await app.listen(3030);
}

bootstrap();

配置完成,我们就可以访问:http://localhost:3000/api/swagger,此时就能看到Swagger生成的文档:

swagger装饰器

  • ApiTags:设置swagger文档标签分类
  • ApiOperation:接口描述信息

我们可以根据Controller来分类, 只要添加@ApiTags就可以,分类接口标签

import { ApiTags } from '@nestjs/swagger';
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';

@ApiTags("用户")
@Controller('post')
export class PostsController {...}
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiOperation, ApiTags } from '@nestjs/swagger';

@ApiTags("用户")
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}
  @ApiOperation({ summary: '创建用户' })
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
  @ApiOperation({ summary: '获取用户' })
  @Get()
  findAll() {
    return this.userService.findAll();
  }
  @ApiOperation({ summary: '根据id获取用户' })
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.userService.findOne(+id);
  }
  @ApiOperation({ summary: '根据id修改用户' })
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.userService.update(+id, updateUserDto);
  }
  @ApiOperation({ summary: '删除用户' })
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
}

图片.png

总结

以上仅仅是Nest入门级别的操作,主要是一些文件创建以及理解、使用,对springBoot熟悉的可能更容易上手,学习Vue和React的可能没这么块,但是感觉思想转变还是需要的。 接触了Nest一段时间了,个人感觉Nest.js作为一个开源项目,对ts的支持也不错,用起来还是很不错的,也是像koa、express的大框架,还是值得去学习的,

我是小白,我们一起学习!