前言:学习一下用Nest.js写接口

一.项目初始化

1.安装脚手架
npm i -g @nestjs/cli
2.创立项目
nest new 项目称号
3. cd 到项目
4.安装依靠
npm i
5.发动项目,监督文件,主动重新编译和重新加载服务器
npm run start:dev
6.检查发动项目发动端口号,进入程序进口文件 src/main.ts 检查
浏览器翻开 http://localhost:3000/

二.中心文件介绍

app.controller.spec.ts 控制器的单元测验,也不知道怎样用的。
app.controller.ts 路由的根本控制器,便是操作逻辑的当地,处理恳求响应。
app.module.ts 运用程序的根模块。
app.service.ts 写数据库查询句子的当地。
main.ts 运用程序的进口文件,它运用中心函数NestFactory创立一个 Nest 运用程序实例。

三.创立一个接口模块,处理恳求

为了快速创立内置验证的 CRUD 控制器,您能够运用 CLI 的CRUD 生成器:nest g resource [name]

这儿依据官方文档提示创立cats模块

nest g resource cats

挑选 REST API风格。

好了,现在src文件目录下多了cats目录,赶紧看看吧。

诶,咱们是创立了cats模块了,咱们有运用吗,有的,在哪里运用了呢?在运用程序的根模块里运用了啊,哦,原来运用nest g resource cats指令创立时,一起运用了,真方便,ok快去看看吧。

四.看看cats模块有啥吧

1.先看controller吧,恳求办法都在这了

首要,映入眼帘的是头部引入了许多许多……啥,不知道是啥。
接下去看吧,有一个@Controller('cats'),这是个装饰器,里边有个参数喔,估计是路由前缀吧,接着看有几个@开头的 post、get、delete的东东,应该是接口办法,嗯,是的,咱们去用APIFox恳求一下

他真的试图教会我用Nest.js写接口耶
我这个get恳求回来了“This action returns all cats”,这不是我写的啊,在哪里的,走,去看看。

@Get()
findAll() {
    return this.catsService.findAll();
}

大哥,我在controller找到这么个玩意,是个service办法耶,让我按着我的Ctrl键点击findAll进去看看先。进到了cats.service.ts文件,里边有一个findAll办法,回来了一串英文,我不明白英文喔,改改才行,我是个大帅b!!!

他真的试图教会我用Nest.js写接口耶

2.你这地址有点单调啊,能不能接收query参数的?

肯定能啊
那行,我给你传个?sex=1,你回来个字给我。
这还不简略? 先在controller头顶哪里的@nestjs/common导入 Req
然后运用

// 导入类型
import { Request } from 'express';
@Get()
  findAll(@Req() req: Request): string {
    console.log('req:', req.query);
    return this.catsService.findAll(req.query.sex as string);
  }
 ps: 这儿获取参数其实能够用 @Query,不知道咋的用了上面这种,哈哈哈

这时分,我打印出了所有的query参数。就差给他回来字了。

cats.service.ts
findAll(sex: string) {
    const sexObj = {
      '0': '男',
      '1': '女',
    };
    return sexObj[sex];
  }

写好了,赶紧在APIFox看看成果。

他真的试图教会我用Nest.js写接口耶

回来成果正确,我还想在post恳求传param参数给你,传传传,传你个头头,好吧,那我想查数据库,总行了吧。

五.衔接mysql数据库

先安装办理数据库typeorm和衔接数据库mysql2

npm install --save @nestjs/typeorm typeorm mysql2

然后在app.module.ts增加配置

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module';
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: '127.0.0.1',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'blog',
      entities: ['dist/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
    CatsModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

好了,数据库衔接成功。
咱们现在是用typeorm来办理数据库,这个库是经过实体来映射到数据库表的,建表咱们曾经是直接在数据库中创立表结构,现在咱们经过实体来创立库。什么意识呢?接下来一起尝试。
在cats文件夹下,有一个entities文件夹,用来放实体的,点开,发现有个ts文件,没错咱们便是在这个ts文件来写实体,也便是数据库表结构。

我写了这些东西:

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
// 建立一个实体映射到数据库表
@Entity('cats')
export class Cat {
  @PrimaryGeneratedColumn()
  id: number;
  @Column({ length: 10 })
  name: string;
}

@Entity(‘cats’)用来修饰说,我这个类是一个实体啊,名字叫cats,其中有字段:id、name,你按照这个结构,在mysql中创立一个表吧。咱们翻开数据库,看下是不是创立了一个叫cats的表呢。

他真的试图教会我用Nest.js写接口耶
好,没什么问题,能够映射。咱们再增加一个desc字段试试

他真的试图教会我用Nest.js写接口耶
没毛病。

六.操作数据库

实体有了,怎样运用呢?
咱们操作数据库正常情况下都是在service中,那么便是去service中运用即可,直接贴代码,咱们向数据库中插入一条数据

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { Cat } from './entities/cat.entity';
@Injectable()
export class CatsService {
  constructor(
    @InjectRepository(Cat)
    private readonly catRepository: Repository<Cat>,
  ) {}
  async create(createCatDto: Partial<CreateCatDto>): Promise<Cat> {
    console.log(createCatDto);
    return this.catRepository.save(createCatDto);
  }
}

啊哈,保存的时分报错了,说什么实体没有引用,那么咱们就引用它,在cats.module.ts

import { Module } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CatsController } from './cats.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Cat } from './entities/cat.entity';
@Module({
  imports: [TypeOrmModule.forFeature([Cat])],
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

service写好了,可是还没调用它呢,去哪里调用啊,controller啊,这么快就忘记了吗。

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  Req,
} from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}
  @Post('/create')
  create(@Body() createCatDto: CreateCatDto) {
    console.log(createCatDto);
    return this.catsService.create(createCatDto);
  }
}

仔细的盆友发现了,说你这CreateCatDto哪里来的?你看看cats目录下有一个dto文件夹,有个create-cat.dto.ts,它便是从这儿来的,哈哈哈

export class CreateCatDto {
  name: string;
  desc: string;
}

好啦好啦,去ApiFox看下成果

他真的试图教会我用Nest.js写接口耶

发送恳求,OK,没报错,咱们去数据库瞧瞧,有没有数据

他真的试图教会我用Nest.js写接口耶

牛逼啊,大哥,原来写接口这么简略啊!!!
感谢观看!!!

七.中间件你应该了解过吧,在这儿应该怎样用呢

老规矩先创立对应的文件,nestjs有方便创立中间件的指令呢

nest g middleware middleware/reqMi

我创立了一个middleware目录下的叫reqMi中间件,命名吗,简略了点。
好,看看中间件现在有什么

import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class ReqMiMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('你好,我是中间件');
    next();
  }
}

我这儿打印了一句话。 那么怎样调用呢?调用中间件有许多种方式,能够参阅官方文档哦。
我现在是在app.module.ts调用

export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(ReqMiMiddleware).forRoutes('cats');
  }
}

ReqMiMiddleware被调用啦,forRoutes(‘cats’),表示为路由为cats前缀的路由增加这个中间件,nestjs中间件,都是经过匹配路由路径来调用的,官方有比较具体的介绍。 咱们现在去看看,是不是调用成功了呀。

他真的试图教会我用Nest.js写接口耶

控制台打印

他真的试图教会我用Nest.js写接口耶

ok,中间件运用成功啦。

八.当咱们的程序出现反常的时分,该怎样办呢?

那么就轮到反常过滤器(Exception filters)出场了。

Nest附带了一个内置反常层,负责处理整个运用程序中所有未处理的反常。当您的运用程序代码不处理反常时,它会被此层捕获,然后该层会主动发送适当的响应给客户端。
咱们现在抛出一个反常看看,会发生什么

import { HttpException, HttpStatus } from '@nestjs/common';
create(@Body() createCatDto: CreateCatDto) {
    console.log(createCatDto);
    // 抛出 http反常
    throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
    return this.catsService.create(createCatDto);
  }

客户端恳求,回来

{
    "statusCode": 403,
    "message": "Forbidden"
}

接下来咱们创立一个通用的反常过滤器

nest g filter filter/http-exception

那么现在src文件夹下,就多了一个filter文件夹,里边有一个http-exception.filter.ts文件,便是接下来要操作的文件了。

import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();
    // 响应给客户端的结构,你也能够自定义结构,像这种 { code: 1, msg: "" }
    // 我通常用0和1来判别恳求是否正确,能够判别status是否等于200 来回来status = 1或0
    response.status(status).json({
      status: status,
      msg: '',
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

官方文档给出了上面这个示例,好了,反常过滤器有了,怎样用呢?

1.给某个路由运用

cats.controller.ts
@Get()
@UseFilters(new HttpExceptionFilter())
findAll() {
    throw new UnauthorizedException();
    return this.catsService.findAll();
}

2.为整个controller运用

@Controller('cats')
@UseFilters(new HttpExceptionFilter())
export class CatsController {}

3.大局范围内运用

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './filter/http-exception.filter';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 大局反常过滤器
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

好了,反常过滤器,咱们会用啦,便是有反常(比方:客户端参数传错了,空参数,类型错误…)咱们就用 throw抛出一个响应值给客户端。仔细的盆友发现了,说,你这个throw后边跟着的是啥啊?
这个其实是 Nest 提供的标准反常继承自HttpException。这些是从@nestjs/common包中公开的,代表了许多最常见的 HTTP 反常:401,403…这些,官方文档有提到,点这儿检查去到文档,向下翻滚就能看到了。

九.管道(参数验证)

方便创立管道文件

 nest g pipe validation pipe

安装相关包

npm i --save class-validator class-transformer

运用

create-cat.dto.ts
import { IsString } from 'class-validator';
export class CreateCatDto {
  @IsString()
  name: string;
  @IsString()
  desc: string;
}
validation.pipe.ts
import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  BadRequestException,
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } from 'class-transformer';
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    console.log('----');
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToInstance(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }
  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

大局运用

main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

十.拦截器 Interceptors

拦截器具有一组有用的功用,这些功用的创意来自面向方面的编程(AOP)技术。它们使以下几点成为可能:

  • 在办法履行之前/之后绑定额外的逻辑
  • 转化从函数回来的成果
  • 转化从函数引发的反常
  • 扩展根本函数行为
  • 依据特定条件彻底掩盖函数(例如,用于缓存目的)

方便创立拦截器,创立一个名为transform的拦截器

nest g interceptor interceptor/transform

十一.Websocket

要开端构建根据 WebSockets 的运用程序,首要安装所需的包:

$ npm i --save @nestjs/websockets @nestjs/platform-socket.io
$ npm i --save-dev @types/socket.io
或许
$ npm i --save @nestjs/websockets @nestjs/platform-socket.io

履行指令创立相应文件

nest g gateway  gateway/chat

十二.表关联查询

比方我有一个category表和emoticon表

import { Emoticon } from 'src/emoticon/entities/emoticon.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('category')
export class Category {
  @PrimaryGeneratedColumn()
  id: number;
  // 类目称号
  @Column({ length: 5 })
  name: string;
  @OneToMany(() => Emoticon, (emoticon) => emoticon.category)
  emoticon: Emoticon[];
}
import { Category } from 'src/category/entities/category.entity';
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
@Entity('emoticon')
export class Emoticon {
  @PrimaryGeneratedColumn()
  id: number;
  // 图片称号
  @Column({ length: 10 })
  name: string;
  // 图片地址
  @Column()
  imgUrl: string;
  @ManyToOne(() => Category, (category) => category.emoticon)
  category: Category;
}

查询分类

findAll() {
    return this.categoryRepository.find({ relations: ['emoticon'] });
}

成果

[
    {
        "id": 2,
        "name": "眼睛",
        "emoticon": [
            {
                "id": 7,
                "name": "测验",
                "imgUrl": "https://gz.bcebos.com/te/hello-1670471717579.jpeg"
            }
        ]
    },
]