Skip to content
NestJs的基础使用

初始化项目

创建项目

js
// 安装脚手架(只需要安装一次,因为这个是全局的)
npm i -g @nestjs/cli
// 创建项目
nest new project-name
// (该过程有个选择包管理工具的,我选的yarn)

启动项目

js
yarn run start:dev
// 可以在浏览器访问localhost:3000  输出helloWorld

控制器和路由

查看命令

js
nest - g;
// 列表描述(name:生成的文件名,alias:简写文件名,description:文件的描述)

生成controller层命令为

js
// 要使用CLI创建控制器,只需执行$ nest g controller [生成的文件夹名]命令。
// 也可以使用简写
nest g co [生成的文件夹名]
// 然后查看src下面的+创建的文件夹
// 项目创建完成查看app.module.ts里面会自动将创建的controller注入进来

在新创建的controller内添加以下内容,确保能被访问到

js
import { Controller, Get } from '@nestjs/common';
// 这里有个小坑(也不算坑,这个Get在我电脑不会自动导入,一度导致我以为报错了,长个记性)
// 配置了下面的之后启动项目访问:localhost:3000/cat 会返回一个JSON对象(数组和对象自动转JSON)
@Controller('cat')
export class CatController {
  @Get() //不写参数默认为空,直接方位/cat就能访问到这个接口
  say() {
    return {
      name: 'cat',
    };
  }
  @Get('/say2') //访问localhost:3000/cat/say2 会返回一个数组(被json转化过的)
  say2() {
    return [1, 2, 3, 4, 5];
  }
}

生成service层命令为

js
// 要使用CLI创建控制器,只需执行$ nest g service [生成的文件夹名]命令。
// 也可以使用简写
nest g s [生成的文件夹名] // 建议和controller放在同一文件夹
// 然后查看src下面的+创建的文件夹
// 项目创建完成查看app.module.ts里面会自动将创建的service注入进来

controller和service结合使用

js
// controller改造如下
import { Controller, Get } from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('cat')
export class CatController {
  // 私有的,只读的service
  constructor(private readonly catService:CatService){}
  @Get()
  say() {
    return this.catService.say();
  }
  @Get('/say1')
  say1() {
    return this.catService.say1();
  }
}

// service改造如下
import { Injectable } from '@nestjs/common';

@Injectable()
export class CatService {
  say() {
    return {
      name: 'catservice',
    };
  }
  say1() {
    return {
      name: [1, 2, 3, 4, 5, 6, 7],
    };
  }
}
// http://localhost:3000/cat/say1 返回{name:[1,2,3,4,5,6,7]}

路由介绍

官方路由示例

js
import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}

本人这里还是以上面个人创建的controller和service为例

以下全是catcontroller.ts内容(都是针对不同接口类型接收参数的介绍)

js
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
} from '@nestjs/common';
import { CatService } from './cat.service';

@Controller('cat')
export class CatController {
  // 私有的,只读的service
  constructor(private readonly catService: CatService) {}

  // 1.@Query
  // 浏览器访问: http://localhost:3000/cat/say?name=%E5%B0%8F%E7%BA%A2&age=18
  // 打印如下:query { name: '小红', age: '18' }
  // 无参数时打印为: query {}
  @Get('/say')
  say(@Query() query: any) {
    // 限定参数类型为any
    console.log('query', query);
    return this.catService.say();
  }

  // 2.动态路由 :id
  // 浏览器访问:http://localhost:3000/cat/99
  // 打印如下: id1 99
  @Get(':id')
  say1(@Param('id') id: number) {
    console.log('id1', id);
    return this.catService.say1();
  }

  // 3.动态路由 /say3/:id
  // 浏览器访问:http://localhost:3000/cat/say3/99
  // 打印如下: id3 99
  @Get('/say3/:id')
  say3(@Param('id') id: string) {
    console.log('id3', id);
    return this.catService.say1();
  }

  // 4.动态路由 无参形式
  // 浏览器访问:http://localhost:3000/cat/say4/99
  // 打印如下: param {id:'99'}
  @Get('/say4/:id')
  // Param参数为空
  say4(@Param() param: { id: number }) {
    console.log('param', param);
    return this.catService.say1();
  }

  // 5.Delete 删除接口
  // APIFOX访问:http://localhost:3000/cat/say5/1 接口类型换成:Delete
  // 打印如下: param { id: '1' }
  @Delete('/say5/:id')
  // Param参数为空
  say5(@Param() param: { id: number }) {
    console.log('param', param);
    return this.catService.say1();
  }

  // 6.Put 新增接口
  // APIFOX访问:http://localhost:3000/cat/say5/1 接口类型换成:Put
  // 打印如下: param { id: '1' }
  @Put('/say6/:id')
  // Param参数为空
  say6(@Param() param: { id: number }) {
    console.log('param', param);
    return this.catService.say1();
  }

  // 7.Post 新增接口
  // APIFOX访问:http://localhost:3000/cat 接口类型换成:Post
  // 参数在接口测试工具的body的json类型加入以下参数: { id: 1, name: '萧寂' }
  // 打印如下: cat { id: 1, name: '萧寂' }
  @Post()
  say7(@Body() cat: any) {
    console.log('cat', cat);
    return this.catService.say1();
  }
}

提供者(也就是Java中的实体类)

在上面创建的cat文件夹内创建interface文件夹,在文件夹内创建cat.interface.ts文件在上面创建的cat文件夹内创建dto文件夹,在文件夹内创建create-cat.dto.ts文件

js
// cat.interface.ts
export interface Cat {
  name: string;
  age: number;
  breed: string;
}

// create-cat.dto.ts
import { Cat } from "../interface/cat.interface";
export class CreateCatDto implements Cat{
  name: string;
  age: number;
  breed: string;
}

// cat.controller.ts
import {
  Body,
  Controller,
  Post,
} from '@nestjs/common';
import { CatService } from './cat.service';
import { CreateCatDto } from './dto/create-cat.dto';

@Controller('cat')
export class CatController {
  // 私有的,只读的service
  constructor(private readonly catService: CatService) {}
  // APIFOX访问:http://localhost:3000/cat 接口类型换成:Post
  // 参数在接口测试工具的body的json类型加入以下参数: { id: 1, name: '萧寂' }
  // 打印如下: cat { id: 1, name: '萧寂' }
  @Post()
  say(@Body() cat: CreateCatDto) {
    console.log('cat', cat); // 在apiFox打印结果和上面一样,只是加了类型校验,但是由于ts只进行类型约束,并不要求参数限定必须一致,因此这里请求的参数与dao和接口类不一致也可以拿到参数,并不会报错,只是不存在的参数不会对其类型进行校验而已了
    return this.catService.say1();
  }
}

中间件

创建中间件

js
nest g mi logger
// 安装完成后在目录中会多出一个logger文件夹

//在app.mudule.ts放入以下内容进行注册绑定中间件
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatController } from './cat/cat.controller';
import { CatService } from './cat/cat.service';
import { LoggerMiddleware } from './logger/logger.middleware';

@Module({
  imports: [],
  controllers: [AppController, CatController],
  providers: [AppService, CatService],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    // 下面这段代码代表先绑定中间件再应用到路由上,当访问cat或者带cat子接口时会被拦截到
    consumer.apply(LoggerMiddleware).forRoutes('cat');
    // 由于同一个接口名可能会有get,put,delete,post等方法,因此可以使用下面方式对指定类型的接口进行拦截
    consumer.apply(LoggerMiddleware).forRoutes({path:'cat',method:RequestMethod.GET}); // 只拦截get请求的cat接口
    // 也可以直接将controller作为参数(这样的话,在当前控制器下面所有的请求都会被拦截到的)
    consumer.apply(LoggerMiddleware).forRoutes(CatController);
    // 如果当前需要拦截的请求较多,可以将controller作为参数,但又不希望其中个别接口被拦截到,可以使用下面方式忽略需要拦截的接口
    consumer.apply(LoggerMiddleware).exclude({path:'cat/:id',method:RequestMethod.GET},{path:'cat',method:RequestMethod.POST}).forRoutes(CatController);
  }
}

// ------------------------------------------------------------------------------------------------
// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    // 每次访问cat接口都会被拦截到,并打印出一下日志
    console.log('正在访问cat接口');
    next();
  }
}

以上就是中间件基础使用方法了

函数式中间件

js
// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('111222', 111222);
    next();
  }
}

// 下面这个是函数式中间件(只有函数式中间件可以注册成为全局中间件)
export function logger(req, res, next) {
  console.log('Request_logger');
  next();
}

注册成为全局中间件,只有函数式中间件可以注册成为全局中间件

js
// 在main.ts进行注册(代码如下)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/logger.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 将中间件注册成为全局中间件
  app.use(logger);
  await app.listen(3000);
}
bootstrap();

异常处理

异常抛出

例如我们输入了一个不存在的地址,就会响应一个404,这个是框架给我们默认返回的一个异常,有时候我们需要手动去抛出一个异常Nest提供了一个内置的HttpException类,为基础异常类,可以很好的帮助我们进行异常的处理

js
// 在controller内其中一个接口如下:
 import {Get,HttpException,HttpStatus} from '@nestjs/common';

  @Get('/xiaoji')
  xiaoji(): string {
    throw new HttpException('服务器异常', HttpStatus.FORBIDDEN);
  }
// 浏览器访问:localhost:3000/cat/xiaoji
// 返回值为: {"statusCode":403,"message":"服务器异常"}

自定义异常及异常的处理

创建并配置异常处理器

js
// 创建异常过滤器
nest g f http-exception
// 创建完成后目录会多出一个http-exception文件夹

// 在新创建的http-exception.filter.ts文件放入以下内容(已经处理好的)
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter<T> 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();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

使用异常处理器

在cat.controller.ts内使用

js
import {
  Body,
  Controller,
  Get,
  HttpException,
  HttpStatus,
  UseFilters,
} from '@nestjs/common';
import { HttpExceptionFilter } from 'src/http-exception/http-exception.filter';

// 在接口使用
  @Get('/xiaoji')
  // 访问到这个接口直接抛出异常(异常的格式为我们上面定制的格式(状态码,时间和接口路径))
  @UseFilters(new HttpExceptionFilter())
  xiaoji(): string {
    // 如果这里不抛出异常,上面这个异常过滤器不会执行,只有手动抛出异常则异常过滤器才会执行
    throw new HttpException('服务器异常', HttpStatus.FORBIDDEN);
  }

全局使用异常过滤器

js
// 例如有的情况有异常,但是我们不能一个个来抛出,例如全局的404异常,因此需要使用全局异常过滤器,方式如下

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/logger.middleware';
import { HttpExceptionFilter } from './http-exception/http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 将中间件注册成为全局中间件
  app.use(logger);
  // 全局注册异常过滤器
  app.useGlobalFilters(new HttpExceptionFilter());
  //
  await app.listen(3000);
}
bootstrap();

// 当访问一个不存在的接口会返回404状态码和时间以及异常的接口路径(改写了默认的样式)

管道

js
管道有两个典型的应用场景:
转换:管道将输入数据转换为所需的数据输出(例如,将字符串转换为整数)
验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常

Nest 自带九个开箱即用的管道,即

  • ValidationPipe
  • ParseIntPipe
  • ParseFloatPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • DefaultValuePipe
  • ParseFilePipe

转换

这里以ParseIntPipe为例,进行数据的转化

js
// 在cat.controller.ts其中一个接口如下内容
import {Get,Param,ParseIntPipe} from '@nestjs/common';
//这个管道可以将接收的参数转为number类型,否则返回的是{id:"123"},转换之后变成{id:123}
//如果参数给个abc导致不能转化成number,则会抛出400错误的异常,代表转化失败
  @Get(':id')
  say1(@Param('id', new ParseIntPipe()) id: number) {
    return {
      id: id,
    };
  }

验证(守卫)

基础使用

守卫在所有中间件之后执行,但在拦截器或管道之前执行。

js
// 创建守卫
nest g gu role
// 在新建的role.guard.ts内放入以下内容
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class RoleGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('RoleGuard');
    return true;
  }
}

// 再新建一个controller
nest g co user
// controller内放入以下内容
import {
  Controller,
  Get,
  Param,
  ParseIntPipe,
  UseGuards,
} from '@nestjs/common';
import { RoleGuard } from 'src/role/role.guard';

@Controller('user')
@UseGuards(RoleGuard) // 使用守卫,当访问下面任意接口时就会触发守卫,并打印('RoleGuard')
export class UserController {
  @Get(':id')
  // @UseGuards(RoleGuard) // 使用守卫,当访问当前接口时就会触发守卫,并打印('RoleGuard')
  getUserDetial(@Param('id', ParseIntPipe) id: number) {
    return {
      id,
    };
  }
}

定义权限,进行路由比对

js
// controller.ts内加入下面代码
import {
  Controller,
  Get,
  Param,
  ParseIntPipe,
  UseGuards,
  SetMetadata,
} from '@nestjs/common';
import { RoleGuard } from 'src/role/role.guard';

@Controller('user')
export class UserController {
  @Get(':id')
  @SetMetadata('role', ['access1', 'access2'])
  @UseGuards(RoleGuard) // 使用守卫
  getUserDetial(@Param('id', ParseIntPipe) id: number) {
    return {
      id,
    };
  }
}

// role.guard.ts加入下面代码
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';

@Injectable()
export class RoleGuard implements CanActivate {
  // 加上反射(可以拿到接口对应相关的一些信息)
  constructor(private reflector: Reflector) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('RoleGuard');
    // 下面的role与controller接口的@SetMetadata('role', ['access1','access2'])第一个参数保持一致
    const roles = this.reflector.get<string[]>('role', context.getHandler());
    console.log('roles', roles); // 打印的roles就是['access1','access2'],刚刚的第二个参数
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    // 上面那个可以在接口上定义权限,然后拿到定义的权限,这里的user代表用户发请求携带的权限,这里可以进行路由比对,有权限放行,没权限返回false
    // 这里是拿到了用户的标识,我们的系统可以查询到用户的权限
    console.log('user',user);
    return true;
  }
}

第二种方式(装饰器模式)

js
// 创建装饰器
nest g d role
// 创建完成后会发现多了个role.decorator.ts文件

// controller.ts
import {
  Controller,
  Get,
  UseGuards,
} from '@nestjs/common';
import { Role } from 'src/role/role.decorator'; // 引入装饰器
import { RoleGuard } from 'src/role/role.guard'; // 引入守卫

@Controller('user')
@UseGuards(RoleGuard) // 使用守卫,下面的所有接口都会触发守卫
export class UserController {
  @Get('user/list')
  @Role('access1', 'access2','access3', 'access4') // 装饰器内的参数就是权限(个人感觉较麻烦不如上面那个)
  getUserList() {
    return [1, 2, 3, 4];
  }
}

// 访问接口得到-----roles [ 'access1', 'access2', 'access3', 'access4' ]

数据库

连接数据库

js
// 安装依赖
yarn add --save @nestjs/typeorm typeorm mysql2
// @nestjs/typeorm这个本人运行上面命令一直安装不上,只好单独安装一下就成功了(yarn add @nestjs/typeorm)


// 安装完成后在app.module.ts进行导入
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'admin',
      database: 'shop',
      // entities: [], // 实体类文件,下面创建完curd会讲怎么写的
      synchronize: true, //是否将实体同步到数据库
      retryDelay:500, // 重试连接数据库的间隔
      retryAttempts: 10, // 重试连接数据库的次数
      autoLoadEntities: true, // 自动加载实体(加上这个就不用上面的entities了)
      logging: true, // 是否开启日志
      logger: 'advanced-console', // 日志
    }),
  ],
})
export class AppModule {}

创建CURD

js
nest g res test
// 选择第一个REST API(遵循rest api的规范)
// 选择y 确定创建curd(增删改查)

// 找到test/entities/test.entity.ts(只要创建了CURD这种文件,一般实体类都是这个位置,那么就可以动态加载实体类了,找到刚刚创建数据库的地方,将entities: [],填充以下内容,用于动态获取实体类),如果autoLoadEntities设置为true了,这个属性可以去掉,autoLoadEntities用于自动加载实体
entities: [__dirname + '/**/*.entity{.ts,.js}'],

在创建完的CURD文件内的实体类编写如下代码

test/entities/test.entity.ts

js
// 定义实体,定义列,定义自增主键
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity() // 定义实体类
export class Test {
  @PrimaryGeneratedColumn()
  id: number; // 自增id列

  @Column()
  name: string;  // 列名

  @Column()
  password: string;

  @Column()
  age: number;
}

在test/test.model.ts里面编写如下代码

js
import { Module } from '@nestjs/common';
import { TestService } from './test.service';
import { TestController } from './test.controller';
// 引入实体
import { Test } from './entities/test.entity';
// 引入orm框架
import {TypeOrmModule} from '@nestjs/typeorm'
@Module({
  // 引入注册
  imports:[TypeOrmModule.forFeature([Test])],
  controllers: [TestController],
  providers: [TestService],
})
export class TestModule {}

保存重启项目会发现MySQL里面的shop这个数据库多了一个test这张表,表内的字段和实体类一一对应

实体类介绍

js
// 定义实体,定义列,定义自增主键
import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn,Generated } from 'typeorm';

@Entity() // 定义实体类
export class Test {
  @PrimaryGeneratedColumn("uuid") // 自增的uuid
  id: number; // 自增id列

  @Column({type:"varchar",length:255}) // 列类型
  name: string;

  @Column()
  password: string;

  @Column()
  age: number;

  // @CreateDateColumn()  // 自增日期
  @CreateDateColumn({ type: 'timestamp' }) // 时间戳类型
  createTime: Date;

  @Generated('uuid') // 自动生成列
  uuid: string;

   @Column({   // 定义枚举类型
    type: 'enum',
    enum: [1, 2, 3, 4],
    default: 1,
  })
  xiaoji: number;
}
js
mysql 所有类型

int, tinyint, smallint, mediumint, bigint, float, double, dec, decimal, numeric, date, datetime, timestamp, time, year, char, varchar, nvarchar, text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob, enum, json, binary, geometry, point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection

列选项

js
    @Column({
        type:"varchar",
        name:"ipaaa", //数据库表中的列名
        nullable:true, //在数据库中使列NULL或NOT NULL。 默认情况下,列是nullable:false
        comment:"注释",
        select:true,  //定义在进行查询时是否默认隐藏此列。 设置为false时,列数据不会显示标准查询,适合用于密码。 默认情况下,列是select:true
        default:"xxxx", //加数据库级列的DEFAULT值
        primary:false, //将列标记为主要列。 使用方式和@ PrimaryColumn相同。
        update:true, //指示"save"操作是否更新列值。如果为false,则只能在第一次插入对象时编写该值。 默认值为"true"
        collation:"", //定义列排序规则。
    })
    ip:string

simple-array 列类型 有一种称为simple-array的特殊列类型,它可以将原始数组值存储在单个字符串列中。 所有值都以逗号分隔

js
@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;
 
    @Column("simple-array")
    names: string[];
}

**simple-json列类型 还有一个名为simple-json的特殊列类型,它可以存储任何可以通过 JSON.stringify 存储在数据库中的值。 当你的数据库中没有 json 类型而你又想存储和加载对象,该类型就很有用了。 例如: **

js
@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;
 
    @Column("simple-json")
    profile: { name: string; nickname: string };
}

数据库的增删改查的使用

axios 接口定义

js
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:3000'
 // 增
export const addUser = (data) => axios.post('/user',data).then(res => res.data)
 // 查
export const getList = (data) => axios.get('/user',{params:data}).then(res => res.data)
 // 删
export const delUser = (data) => axios.delete(`/user/${data.id}`).then(res => res.data)
 // 改
export const updateUser = (data) => axios.patch(`/user/${data.id}`,data).then(res => res.data)

nest后端代码为

实体类为

js
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class CreateUserDto {
	@Column()
    name:string
    @Column()
    desc:string
}

controller代码如下

js
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
 
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}
 
 // 增
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
 // 查
  @Get()
  findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {
    return this.userService.findAll(query);
  }
 // 改
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.userService.update(+id, updateUserDto);
  }
 // 删
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
}

service代码如下

1.引入 InjectRepository typeOrm 依赖注入 接受一个实体 2.引入类型 Repository 接受实体泛型 3.Like 用于模糊查询 4.save 保存 find 查询 update 更新 delete 删除

js
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Repository, Like } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Injectable()
export class UserService {
  constructor(@InjectRepository(User) private readonly user: Repository<User>) { }
	// 增
  create(createUserDto: CreateUserDto) {
    const data = new User() // 创建实体类对象
    data.name = createUserDto.name // 实体类的name赋值为传来的name
    data.desc = createUserDto.desc // 实体类的desc赋值为传来的desc
    return this.user.save(data) // 保存(每次保存都相当于添加一次)
  }
 // 查
  async findAll(query: { keyWord: string, page: number, pageSize: number }) {
    const data = await this.user.find({ // 查询
      where: {
        name: Like(`%${query.keyWord}%`) //模糊查询
      },
      select:['id','name'], // 指定查询出来的字段
      order: {
        id: "DESC" // 倒序
      },
      skip: (query.page - 1)* query.pageSize, // skip跳过,从0开始
      take:query.pageSize, // 需要展示的每页的数量
    })
    const total = await this.user.count({  // 总数量
      where: {
        name: Like(`%${query.keyWord}%`)
      },
    })
    return {
      data,
      total
    }
  }
 // 改
  update(id: number, updateUserDto: UpdateUserDto) {
    return this.user.update(id, updateUserDto)
  }
 // 删
  remove(id: number) {
    return this.user.delete(id)
  }
}

Module代码如下

js
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import {TypeOrmModule} from '@nestjs/typeorm'
import { User } from './entities/user.entity';
@Module({
  imports:[TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

[多表联查的使用]

新增了一个接口 export const addTags = (data) => axios.post('/user/add/tags',data).then(res => res.data)

后端Nestjs 1.新建一个 tags.entity.ts 定义Tags的数据表

js
import { Column, Entity, PrimaryGeneratedColumn, BeforeInsert, CreateDateColumn, Generated, OneToOne, JoinColumn, ManyToOne } from 'typeorm'
import { User } from './user.entity'
@Entity()
export class Tags {
    @PrimaryGeneratedColumn()
    id: number
 
    @Column()
    tags:string
 
    @ManyToOne(()=>User,(user)=>user.tags)
    @JoinColumn()
    user:User
}

Modal 需要关联tag表

js
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import {TypeOrmModule} from '@nestjs/typeorm'
import { User } from './entities/user.entity';
import { Tags } from './entities/tags.entity';
// import { example } from './entities/tags.entity';
@Module({
  imports:[TypeOrmModule.forFeature([User,Tags])],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

然后user表跟tags表进行关联

js
import { Column, Entity, PrimaryGeneratedColumn, BeforeInsert, CreateDateColumn, Generated, OneToOne, JoinColumn, OneToMany } from 'typeorm'
import { Tags } from './tags.entity'
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number
 
  @Column({ type: "varchar", length: 255 })
  name: string
 
 
  @Column({ type: "text" })
  desc: string
 
  @Generated('uuid')
  uuid: string
 
  @CreateDateColumn({ type: "timestamp" })
  createTime: Date
 
  @OneToMany(() => Tags, (tags) => tags.user)
  tags:Tags[]
  // example: example
}

这儿我们解释一下 OneToMany 和 ManyToOne的用法 对于用户来说一个用户可以拥有多个tag 他们的关系是一对多 OneToMany 对于tag来说他们是多个tag指定单个用户 所以是 ManyToOne 在这里插入图片描述OneToMany 接受两个参数 第一个参数是个函数返回关联的类 所以在user表关联tag 第二个参数 创建双向关系 ManyToOne 用法一样

js
@OneToMany(() => Tags, (tags) => tags.user)

保存该关系

沿用上一章的代码增加Controller 增加 addTags

js
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
 
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}
 
  @Post('/add/tags')
  addTags (@Body() params:{tags:string[],userId:number}) {
    return this.userService.addTags(params)
  }
 
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }
 
  @Get()
  findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {
    return this.userService.findAll(query);
  }
 
 
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.userService.update(+id, updateUserDto);
  }
 
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
}

service 增加 addTags 方法

js
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Repository, Like } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Tags } from './entities/tags.entity';
// import { example } from './entities/tags.entity';
@Injectable()
export class UserService {
  constructor(
  @InjectRepository(User) private readonly user: Repository<User>,
  @InjectRepository(Tags) private readonly tag: Repository<Tags>
  ) { }
 
//通过前端传入的userId 查到当前id 的用户信息,然后拿到前端传入的tags [tag1,tag2,tag3]
// 进行遍历 给tag实例进行赋值 然后调用保存方法添加tag 添加完之后 通过 tagList 保存该tag类
// 最后把tagList 赋给 user类的tags属性 然后重新调用save 进行更新
 
  async addTags (params:{tags:string[],userId:number}) {
    const userInfo = await this.user.findOne({where:{id:params.userId}})
    const tagList:Tags[] = []
    for (let i = 0;i<params.tags.length;i++) {
       let T =  new Tags()
       T.tags = params.tags[i];
       await this.tag.save(T)
       tagList.push(T)
    }
    userInfo.tags = tagList;
    console.log(userInfo,1)
    return this.user.save(userInfo)
  }
 
  async create(createUserDto: CreateUserDto) {
    const data = new User()
    // const ex = new example()
    data.name = createUserDto.name
    data.desc = createUserDto.desc
    // await this.example.save(ex)
    return this.user.save(data)
  }
 
  async findAll(query: { keyWord: string, page: number, pageSize: number }) {
    const data = await this.user.find({
      //查询的时候如果需要联合查询需要增加 relations
      relations:['tags'],
      where: {
        name: Like(`%${query.keyWord}%`)
      },
      order:{
        id:"DESC",
      },
      skip: (query.page - 1) * query.pageSize,
      take: query.pageSize
    })
    const total = await this.user.count({
      where: {
        name: Like(`%${query.keyWord}%`)
      },
    })
    return {
      data,
      total
    }
  }
 
  update(id: number, updateUserDto: UpdateUserDto) {
    return this.user.update(id, updateUserDto)
  }
 
  remove(id: number) {
    return this.user.delete(id)
  }
}

事务