首页 > 其他分享 >NestJs 快速入门

NestJs 快速入门

时间:2024-08-30 18:37:05浏览次数:10  
标签:NestJs 入门 export user import users 快速 class nestjs

  npm i -g @nestjs/cli,nest new car-report 快速创建car-report 项目。src目录下面有main,controller,service,module文件。main.ts是项目的入口,它引入AppModule,创建服务,监听3000端口。AppModule是一个注解@Module()的类,也称为app模块。由于项目启动时引入AppModule,它也称为根模块。模块有什么作用,体现在@Module的参数上,import引入其它模块,controllers提供控制器,处理请求和响应。根目录引入其它模块,其它模块也提供了controller, 其它模块再引入其它模块,它们也提供了controller,通过import 构建起了整个应用,对应的controller分别处理各个模块的请求,职责清晰,因此模块是构建NestJs应用的基石。

  控制器是注解了@Controller的类,类的每一个方法再注解@Get,@Post等,该方法就处理get和post请求。项目启动,从AppModule开始,找到所有import的module,再根据每一个module提供的controller,NestJs会构建一个路由映射表,哪一个请求和哪个Controller的哪个方法相对应。当客户端发来请求时,NestJs就能知道调用哪个方法。AppController

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

  @Controller()没有参数,@Get也没有参数,就相当于根路径。路由映射就是 / ---> getHelllo()。当请求 / 时会调用getHello方法,方法也称为路由处理器。npm run start:dev 启动服务,postman get请求localhost:3000,返回hello world。controller调用了appService,这是一种设计模式,controller 只是负责接受客户端请求,然后再调用service来处理业务逻辑,业务逻辑处理完之后调用repository,repository负责和数据库打交道。现在创建一个messages模块。新建messages目录,再在目录里面新建messages.controller.ts,messages.module.ts, messages.service.ts, messages.repository.ts。messages.repository.ts 假设从数据库中查到数据,

export class MessageRepo {
    findOne() {
        return {
            message: 'first NestJs demo'
        }
    }
}

  messages.servcie.ts调用repository的方法,由于repository是个类,所以在service 中,要先创建实例,当然是在构造函数中

import { MessageRepo } from './messages.repository';

export class MessagesService {
    private messageRepo: MessageRepo;
    constructor() {
        this.messageRepo = new MessageRepo();
    }
    getMessages() {
       return this.messageRepo.findOne();
    }
}

  controller 调用service,也要在messages.controller.ts的类的构造函数中,创建一个service的实例。

import { Controller, Get } from '@nestjs/common';
import { MessagesService } from './messages.service';

@Controller('messages')
export class MessageController {
    messagesService: MessagesService

    constructor() {
        this.messagesService = new MessagesService()
    }
    
    @Get()
    getMessages() {
        return this.messagesService.getMessages()
    }
}

  @Controlloer 接受一个参数messages,表示这个Controller处理以messages开头的请求, 由于@Get没有参数 ,/messages请求就会调用getMessages方法。messages.module.ts,注册controller,

import { Module } from '@nestjs/common';
import { MessageController } from './messages.controller';

@Module({
  controllers: [MessageController]
})
export class MessagesModule {}

  AppModule 中,import MessagesModule,

import { Module } from '@nestjs/common';
import { MessagesModule } from './messages/messages.module';

@Module({
  imports: [MessagesModule],
})
export class AppModule { }

  npm run start:dev,启动服务,AppModule  import了MessagesModule,找到了MessagesModule 注册的controller,建立了/messages到getMessages的映射,postman请求http://localhost:3000/messages, 返回了{ "message": "first NestJs demo" }。当controller的方法返回基本数据类型时,直接返回。当返回对象或数组时,会序列化为json。

  以上开发方式和普通的Express开发,没有什么区别,只不过把普通对象转化成类。NestJs 有一个依赖注入的概念,不需要在构造函数中手动创建对象,而是声明需要什么类型的对象(依赖),程序运行时,NestJs自动注入这个对象。如果了解Java Spring框架的依赖注入,NestJs的依赖注入有点别扭。程序启动的时候,AppModule只能import其它module,所以其它module要告诉NestJs,它要提供哪些依赖,这就是@Module的provider属性。Provider的意思是 things that can be used as dependcies for other classes。假设provider是 MessagesService, NestJs就会找到MessagesService,需要注意的是MessagesService类需要被@Injectable()修饰,表明让NestJs来管理这个类。你告诉NestJs提供这些依赖,但你又不让NestJs来管理这些东西,那肯定不行。假设MessagesService又依赖MessagesRepo,MessagesRepo也要被Injectable()修饰,并且添加到provider中。controller 依赖service, 所以MessagesController 的构造函数修改如下

constructor(messageService: MessagesService) {// 让NestJs注入MessagesService 对象
    this.messagesService = messageService;
}

  service依赖repository,MessagesService 的构造函数修改如下

constructor(messageRepo: MessageRepo) { // 让NestJs注入MessageRepo对象
    this.messageRepo = messageRepo;
}

  MessagesModule的provider告诉NestJs 它要提供MessagesService的依赖和MessageRepo的依赖

import { Module } from '@nestjs/common';
import { MessageController } from './messages.controller';
import { MessagesService } from './messages.service';
import { MessageRepo } from './messages.repository';

@Module({
  controllers: [MessageController],
  providers: [MessagesService, MessageRepo]
})
export class MessagesModule {}

  既然NestJs提供MessagesService和MessageRepo依赖。用@Injectable()修饰MessagesService和MessageRepo

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

@Injectable()
export class MessagesService {}

@Injectable()
export class MessageRepo {}

  NestJs 启动后,创建一个依赖注入的容器或注入器(Nest DI Container/Injector),它是一个对象,包含class列表和他们的依赖。它会查找项目中的provider,然后把它们注册到容器中。然后容器开始分析类的依赖(看构造函数的参数),然后创建一些内部记录来表示依赖,比如,service类依赖repositroy, repositroy类它没有依赖。然后在某些时候, 告诉DI容器创建controller的实例,DI就会分析contoller的依赖,它依赖Service,service再依赖resposotry。DI先创建reposiory的实例,然后再创建service的实例, 最后创建controller的实例,并返回。DI第一步是分析类的依赖,第二步是创建contoller实例并返回。controller实例是自动创建的

   AppModule怎么使用MessagesModule中的service 呢?service属于自己的module,当一个service 要被其他module 使用时,它要被export 出去。MessagesModule export 出去MessagesService

@Module({
  controllers: [MessageController],
  providers: [MessagesService, MessageRepo],
  exports: [MessagesService]
})
export class MessagesModule {}

  一个模块要使用另一个模块的service,就要把另一个模块引入,AppModule要import MessagesModule, 然后在使用service的地方依赖注入到构造函数中,AppModule已经import 过了MessagesModule,假设AppService使用MessagesService,AppService修改如下

import { Injectable } from '@nestjs/common';
import { MessagesService } from './messages/messages.service';

@Injectable()
export class AppService {
  constructor(private messageService: MessagesService) {}

  getHello(): string {
    return 'Hello World!' + this.messageService.getMessages().message;
  }
}

  写一个真实user的登录注册。首先要有数据库,用TypeORM操作MySQL,npm install --save @nestjs/typeorm typeorm mysql2 安装依赖。TypeORM有实体(Entity)的概念,对应MySql的一张表,它是一个类,代表表名,属性代表列。创建users目录,user目录下创建users.entity.ts

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  admin: boolean;

  @Column()
  email: string;

  @Column({default: true})
  password: string;
}

  连接数据库,在AppModule

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/users.entity';

@Module({
  imports: [TypeOrmModule.forRoot({
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'root',
    password: '123',
    database: 'test',
    entities: [User],
    synchronize: true,
  })]
})
export class AppModule { }

  TypeORM模块的import方式不太一样,它是调用forRoot方法返回一个模块,这种模块称为动态模块,相对应的,MessagesModule 称为静态模块。静态模块功能是固定的,由@Module定义,import的时候,直接import 模块名,不用调用方法。不管是静态模块还是动态模块,模块一旦创建,它是单例的,存在某个地方,但又不全局可用(every module has its own set of imported modules in its scope)。Typeorm模块在AppMoule 中创建,它就已经存在,连接到数据库了,users模块中只使用user功能,所以在users中,Typeorm.forfeature('user'),让user功能在users模块中使用。当使用一个动态module 的时候,要么配置module的行为,要么引入某个feature。users目录下,创建users.module.ts

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users.entity';
import { UsersService } from './users.service';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule { }

  创建users.controller.ts

import { Controller, Get, Post } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
    constructor(private usersService: UsersService) { }

    @Post('/signup')
    async createUser() {}

    @Post('/signin')
    async singin() {}
    
    @Get('/:id')
    async getUser() {}
}

  创建users.service.ts,本来是要创建users.repository.ts的,但有了实体,TypeORM会自动创建对应的repostiory对象来操作表,不用手动创建Repository类了,只需要在service中注入创建好的repostiory对象,users.service.ts 如下

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./users.entity";

@Injectable()
export class UsersService {
    constructor(
        @InjectRepository(User)
        private usersRepository: Repository<User>,
      ) {}
}

  Repositoy类型使用泛型Repository<User>。创建user就要接收客户端传递过来的参数,NestJs有@Req装饰器,只要函数参数被@Req修饰,参数就是request请求对象,函数中可以获取到request.body, request.params, request.query等对象,由于这些对象非常常见,NestJs为每一个对象都创建装饰器,@Body,@Params, @Query。@Body修饰的参数直接就是客户端传递过来的数据。那参数是什么类型?客户端传递过来emai和password, 那就创建一个类,有email 和password属性,参数就是这个类类型。创建create-user.ts

export class CreateUser {
    email:string;
    password: string
}

  users controller中的createUser 函数

@Post('/signup')
async createUser(@Body() body: CreateUser) {}

  CreateUser类也称为DTO(Data Transfer object)类,用于接受客户端传递过来的数据。客户端传递过来email和password,它是以字符串(JSON.stringfy())的形式传递过来的,服务端需要JSON.parse进行解析,然后创建CreateUser一个实例对象,把解析出来的数据赋值给对象,然后再把对象赋值给body,body是CreateUser的一个实例对象。能接受数据了,还要对数据进行验证。NestJs有一个pipe 的概念,数据到达路由处理函数之前要经过一系列过程,pipe就是其中之一,可用于验证。NestJ提供ValidationPipe和两个npm包。Class-transformer包把plain object转化成一个类的实例。Class-validation 包使用注解验证,然后把验证结果给到validation pipe。npm install class-transformer class-validator, createUser 函数@Body(ValidationPipe),CreateUser 类添加验证

import { IsString, IsEmail } from 'class-validator';

export class CreateUser {
    @IsEmail()
    email:string;
    
    @IsString()
    password: string
}

  validation 还有一个group的概念,按照条件进行验证,给ValidationPipe进行传参group: ['create],然后在CreateUser中添加验证条件的时候,也添加group: ['create]。如果对每个客户端数据都进行验证,在每一个路由处理函数都添加ValidationPipe,比较麻烦,可以开启全局验证。useGlobalPipes(new ValidationPipe()), useGlobalPipes对所有的请求都应用它包含的pipe。ValidationPipe 验证每一个请求,如果DTO类没有添加验证规则,也不会对请求进行验证。main.ts

import { ValidationPipe } from "@nestjs/common";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true
  }))
  await app.listen(3000);
}
bootstrap();

  验证的过程如下

  有个问题,validation pipe是怎么知道使用哪个验证的规则的来验证哪一个路由的?尤其是typescript 编译成js,类型擦除后?ts 配置emitDecoratorMetadata, 把类型信息添加到js中, 编译后在js代码,dist目录,users下面的users.controller.js

exports.UsersController = UsersController;
__decorate([
    (0, common_1.Post)('/signup'),
    __param(0, (0, common_1.Body)(common_1.ValidationPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [create_user_1.CreateUser]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "createUser", null);

  现在把接受到的参数存储到数据库,调用userService中的create 方法,

async createUser(@Body(ValidationPipe) body: CreateUser) {
    await this.usersService.create(body.email, body.password)
    return {message: 'create successfully'}
}

  在UsersService类下面添加create方法,Typeorm生成的repository对象有create,save,insert,update,delete,find等方法。向数据库中插入数据,有两种实现方式,create之后调用save,

async create(email: string, password: string) {
  const user = this.usersRepository.create({ email, password, admin: true });
  await this.usersRepository.save(user);
}

  和直接调用insert。

async create(email: string, password: string) {await this.usersRepository.insert({ email, password, admin: false })
}

  在AppModule中import UserModule, npm run start:dev启动服务,

   同样的,update也有两种方式,先findOne,再调用save方式,或直接调用update 方法。delete也有两种方式,先findOne,再调用remove方法,或直接调用delete方法。为什么会有save和remove 方法呢?因为在Entity 中可以定义一些hooks,@AfterInsert, @AfterUpdate, 只有调用这两个方法的时候,它们才会执行,直接调用insert,update,delete不会执行,但save 方法,性能可能不高,因为,当实体不存在时,它执行insert操作,当实体存在时,它执行update操作,每天都要执行两次query查询,先find,再insert或update。

  登录singin,都会返回token,以后每一个请求都带有token,就知道谁在请求。npm install --save @nestjs/jwt。在UserModule中,

import { JwtModule } from '@nestjs/jwt';
export const secret = 'addfsdsfdf'

imports: [TypeOrmModule.forFeature([User]), JwtModule.register({
  global: true,
  secret: secret,
  signOptions: { expiresIn: '1h' },
})]

  再UserService中注入JwtService,实现signIn方法

import { JwtService } from '@nestjs/jwt';
import { Injectable, UnauthorizedException } from "@nestjs/common";

constructor(
  @InjectRepository(User)
  private usersRepository: Repository<User>,
  private jwtService: JwtService
) { }

async signIn( email: string, password: string) {
    const user = await this.usersRepository.findOne({where: { email }});
    if (user?.password !== password) {
      throw new UnauthorizedException();
    }
    const payload = { sub: user.id };
    return await this.jwtService.signAsync(payload)
  }

  在UserController 中

@Post('/signin')
async singin(@Body(ValidationPipe) body: CreateUser) {
    const token = await this.usersService.signIn(body.email, body.password)
    return {
        access_token: token
    };
}

  getUser方法,只有用户登录,才能调用访问,有些路由是要保护起来的,如果没有登录,就不能访问,这要用到guard,有一个canActivate(), 返回true or false,true表示允许访问,false表示不允许访问。用户登录就是验证token,创建AuthGuard.ts

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { secret } from './users.module';
import { Request } from 'express';

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(private jwtService: JwtService) { }

    async canActivate(context: ExecutionContext): Promise<boolean> {
        const request = context.switchToHttp().getRequest();
        const token = this.extractTokenFromHeader(request);
        if (!token) {
            throw new UnauthorizedException();
        }
        try {
            await this.jwtService.verifyAsync(token,{secret});
        } catch {
            throw new UnauthorizedException();
        }
        return true;
    }

    private extractTokenFromHeader(request: Request): string | undefined {
        const [type, token] = request.headers.authorization?.split(' ') ?? [];
        return type === 'Bearer' ? token : undefined;
    }
}

  getUser方法,那就使用UseGuard进行保护

@Get('/:id')
@UseGuards(AuthGuard)
async getUser() { }

  实现getUser,获取id参数,用@Param装饰器,

async getUser(@Param('id') id: string) {
    return await this.usersService.findOne(id);
}

  userService实现findOne,

findOne(id: number) {
  if (!id) return null;
  return this.usersRepository.findOneBy({ id });
}

  但这时有一个问题,controller中调用findOne的id是string类型,但service中,id接受的是number类型,这是可以用pipe,@param('id', ParseIntPipe) 把id转换成int 类型。

async getUser(@Param('id', ParseIntPipe) id: number) {
    return await this.usersService.findOne(id);
}

  pipe通常做两件事情,一个是类型转换,一个是验证用户的输入。在以上的方法中,抛出了异常,比如throw new UnauthorizedException(),NestJs有一层Exception filter,当应用程序中抛出了异常,而没有被捕获时,它会把异常转换成合适response,比如 throw NotFoundExeption 时, nextJs会返回404,not found。对exception 进行过滤,返回合适的响应。

  但返回值中有password,应该要去掉才对,这样用到拦截器。拦截器实现一个NestInterface,intercept 里面正常写,拦截请求,return next.handle() 对拦截响应,对路由处理器的返回值进行处理。它返回的是rxjs的observer,有map等操作,map中的data 就是路由处理器返回的data。 返回值去掉password,创建serialInteceptor.ts

import { CallHandler, ExecutionContext, NestInterceptor, UseInterceptors } from "@nestjs/common";
import { Observable, map } from "rxjs";

export class SerializeIntercepter implements NestInterceptor {
    intercept(context: ExecutionContext,
        next: CallHandler<any>): Observable<any> | Promise<Observable<any>> {
        return next.handle().pipe(
            map(data => {
                Reflect.deleteProperty(data, 'password'); // true
                return data;
            })
        )
    }
}

  getUser用userInterceptor.

@Get('/:id')
@UseGuards(AuthGuard)
@UseInterceptors(SerializeIntercepter)
async getUser(@Param('id', ParseIntPipe) id: number) {
    return await this.usersService.findOne(id);
}

  可以把拦截器包起来,形成一个装饰器,serialInteceptor.ts

export function Serialize() {
    return UseInterceptors(SerializeIntercepter);
}

  getUser 去掉@UseInterceptors(SerializeIntercepter), 直接使用@Serialize()。再创建一个report 模块,一辆汽车的报告,用户创建它,admin 用户批准它。nest cli 提供了一些命令来创建module,controller和service, nest g module reports,nest g controller reports, nest g service reports,手动在reports目录建reports.entity.ts

import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';

@Entity()
export class Report {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({default: false})
  approved: boolean;

  @Column()
  price: number;

  @Column()
  year: number

  @Column()
  mileage: number
}

  然后在AppModule 中,Typeorm的配置项entities中,添加Report

 entities: [User, Report],

  用户创建report, createReport 中要知道用户的信息,admin批准report,那还要判断登录的用户是不是admin,如要不是,批准的api就不能被调用,需要创建AdminGuard。从客户端请求中,只能得到userId,所以其它信息还要从数据库里面取。这里要用到中间件,这是由中间件,guard,拦截器的执行顺序决定的。

   在中间件中,调用userService,获取到用户信息,然后把信息添加到request对象上,后面执行的guard,拦截器,路由处理器都能获取到request对象上在user信息。在src目录下,创建current-user.middlewire.ts

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { UsersService } from './users/users.service';
import { JwtService } from '@nestjs/jwt';
import { secret } from './users/users.module';

@Injectable()
export class CurrentUserMiddleware implements NestMiddleware {
    constructor(private user: UsersService, private jwtService: JwtService) { }

    async use(req: Request, res: Response, next: NextFunction) {
        const [, token] = req.headers.authorization?.split(' ') ?? [];
        if (token) {
            try {
                const result = await this.jwtService.verify(token, { secret });
                const user = await this.user.findOne(result.sub);
                // @ts-ignore
                req.currentUser = user;
            } catch (error) {
                console.log(error)
            }
        }
        next();
    }
}

  中间件的使用比较特别,使用中间件的module要实现NestModule, 在configure中配置,比如在AppModule中配置中间件

import { CurrentUserMiddleware } from './current-user.middlewire';
import { MiddlewareConsumer, NestModule } from '@nestjs/common';

export class AppModule implements NestModule  {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(CurrentUserMiddleware)
      .forRoutes('*'); // 或for('/reports')
  }
}

  由于中间件在AppModule中引入的,使用了UserService,UserModule还要exports 出去UserService。

@Module({
  // ....
  providers: [UsersService],
  exports: [UsersService]
})
export class UsersModule { }

  现在createReport可以获取到user信息了,但怎么在report中保存user信息呢?这涉及到了关系,report和user有1对多的关系,

  在user实体中, 添加属性

@Entity()
export class User {
  // ...
  @OneToMany(() => Report, (report) => report.user )
  reports: Report[] // 数组表示多个report
}

  在report 实体添加属性

@Entity()
export class Report {
  // ...
  @ManyToOne(() => User, (user) => user.reports)
  user: User
}

  oneToMany或ManyToOne为什么第一个参数是函数。这是因为,User Entity中使用Report Entity, Report Entity 中又使用User Entity,循环依赖了,不能直接使用,所以要用函数包起来,以后执行,而不是加载文件的时候执行。第二个函数参数的意思是关联的实体,返回值是定义的实体, 通过关联的实体report怎么找回到定义report的实体(User),report entity 有一个user字段,就是定义reports的实体(User实体中有reports属性)。Report实体有一个user字段,存储report时,给report的user属性赋值一个user实体,当真正存储到数据库时,会从user实体中取出id,存储到数据库。ReporstController

import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../users/AuthGuard';
import { ReportsService } from './reports.service';

@Controller('reports')
export class ReportsController {
    constructor(private readonly reportsService: ReportsService) { }

    @Post()
    @UseGuards(AuthGuard)
    async createReport(@Body() body: any, @Req() req: any) { //body 的类型本来是一个DTO类型,简单起见,写了any
        const userReturn = await this.reportsService.create(body, req.currentUser)
        return userReturn
    }
}

  ReportsService

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from 'src/users/users.entity';
import { Repository } from 'typeorm';
import { Report } from './reports.entity'

@Injectable()
export class ReportsService {
    constructor(
        @InjectRepository(Report)
        private reportsRepository: Repository<Report>,
    ) { }
    create(reportDto: any, user: User) {
        const report = this.reportsRepository.create(reportDto);
        // @ts-ignore
        report.user = user;
        return this.reportsRepository.save(report)
    }
}

  由于在Service中注入了Report,所以在ReportsModule中 imports: [TypeOrmModule.forFeature([Report])],

  repository的save方法把整个关联的user 实体都返回了。还有就是controller 接收了@req参数,能不能也像@Body一样,直接获取user?这要自定义一个参数装饰器createParaDecorator. 在src目录下,currentUser.ts

import {createParamDecorator, ExecutionContext} from '@nestjs/common'

export const CurrentUser = createParamDecorator(
    (data: never, context: ExecutionContext) => {
        const request = context.switchToHttp().getRequest();
        return request.currentUser
    }
)

  controller

import { CurrentUser } from '../currentUser';
import { User } from '../users/users.entity';

@Post()
@UseGuards(AuthGuard)
async createReport(@Body() body: any,  @CurrentUser() user: User) {
    const userReturn = await this.reportsService.create(body, user)
    // @ts-ignore
    const newUser = { ...userReturn, userId: userReturn.user.id };
    // @ts-ignore
    delete newUser.user;
    return newUser
}

  现在写一个approve, 就是把report的approve属性,改成true.  它需要admin权限,写一个AdminGuard。在report目录下,admin.guard.ts

import { CanActivate,  ExecutionContext} from '@nestjs/common'

export class AdminGuard implements CanActivate {
    canActivate(context: ExecutionContext): boolean {
        const request = context.switchToHttp().getRequest();
        if(!request.currentUser) {
            return false
        }

        if(request.currentUser.admin) {
            return true
        } else {
            return false
        }
    }  
}

  ReportsController 添加一个patch 路由

@Patch('/:id')
@UseGuards(AdminGuard)
async approveReport(@Param('id') id: number, @Body() body: { approved: boolean }) {
    return await this.reportsService.changeApproval(id, body.approved);
}

  ReportsService 添加 changeApproval 方法

async changeApproval(id: number, approved: boolean) {
    const report = await this.reportsRepository.findOne({ where: { id } })
    if (!report) {
        throw new NotFoundException('not found')
    }

    report.approved = approved;

    return this.reportsRepository.save(report)
}

  当查询条件比较复杂的时候,就不能简单地用findOne和find了,就要使用createQueryBuilder,比如查询price是5000, mileage 也是5000等。在Controller 中,

@Get()
async getOneReport() {
   return this.reportsService.getReport();
}

  在Service 中

async getReport() {
  return await this.reportsRepository.createQueryBuilder('report')
        .where('report.price= :price', {price: 5000})
        .andWhere("report.mileage = :mileage", { mileage: 5000 })
        .getOne()

}

  当fetch reprot时,不会自动fetch user。同样的,当fetch user的时候,也不会自动fetch report。 

  config 配置环境,npm i @nestjs/config, @nestjs/config内部使用dotenv。Dotenv的目的是, 把不同的环境变量(命令行定义的环境变量, .env 文件定义的环境变量)收集起来, 形成一个对象(process.env),返回给你。 如果各个方法定义的环境变量有冲突,命令行中定义的优先级高。

   @nestjs/config 提供了依赖注入的功能。 每一个环境不同的.env 文件,然后,configroot.forRoot() 加载不同的配置文件(命令行配置env环境变量),

  provider 定制化:当provider提供一个类名时,是标准引入,提供一个对象时,是定制引入。对象的一个key是provide,表示提供什么,真实的作用就是一个token标示符。另外一个key是useValue或useClass或useFactory, 当NestJs遇到provider指定的token后,就会用useValue指定的value,或useClass指定的类的实例对象,或调用useFactory,来注到token中。标准引入其实就是provide和useClass都是类名。providers: [{provide: ReportsService, useClass:ReportsService}]

 

标签:NestJs,入门,export,user,import,users,快速,class,nestjs
From: https://www.cnblogs.com/SamWeb/p/18355879

相关文章

  • Yolov5入门介绍(官网文档学习笔记)
    一、yolov5是什么yolov5是yolo的第五次迭代,旨在提供高速、高精度的目标检测模型官方文档:ComprehensiveGuidetoUltralyticsYOLOv5-UltralyticsYOLODocs二、yolov5的优点1、高速、高精度 (例如R-CNN目标检测有两部:先生成候选框再分类)2、基于pytorch搭建,使用于各......
  • C++ 快速输入的优化与缓冲区管理(竞赛必用)
    在编程竞赛和性能敏感的场景中,数据输入的效率往往直接影响到程序的运行速度。为了优化输入操作,我们可以通过手动设定缓冲区的方式来提升输入的速度。本文将详细介绍两种不同的快速输入方案:手动设定缓冲区大小的方案与系统默认缓冲区大小的方案,并结合二进制位数与可表示数据范围......
  • 【CSP】坐标变换2(问题拆解,快速输入,知识补充)
    1.题目背景与任务分析题目背景本题要求对二维平面上的点进行指定角度的旋转,并输出旋转后的坐标,要求精确到小数点后六位。这类题目广泛用于考察选手对数学计算、坐标变换以及编程语言中浮点数处理的能力。任务明确输入:多个坐标点及旋转角度。输出:旋转后的新坐标,精确到小数......
  • 动态规划 之《从入门到入土》
    bushi动态规划的几个模板and例题背包问题01背包顾名思义,一个东西只有选和不选两种选择。求体积一定的包里能放的最大质量。 for(inti=1;i<=n;i++) { for(intj=m;j>=w[i];j--)//w[i]表示物品i的体积 { f[j]=max(f[j],f[j-w[i]]+v[i]);//v[i]表示物品i的质......
  • 快速搭建一款可交互式的「地理空间」应用
    GreppoGitHub上的开源Python框架,可快速搭建一款可交互式的「地理空间」应用。该框架提供了一整套完整工具包,让你可以轻松打通数据、算法、可视化UI等模块,构建一款交互式应用。https://github.com/greppo-io/greppo ......
  • Pinia入门(快速上手)
    定义一个Store 在深入了解核心概念之前,我们需要知道Store是使用 defineStore() 定义的,并且它需要一个唯一名称,作为第一个参数传递:import{defineStore}from'pinia'//useStore可以是useUser、useCart之类的任何东西//第一个参数是应用程序中store的唯一i......
  • HTTP协议入门
    HTTP协议入门参考:http://www.ruanyifeng.com/blog/2016/08/http.html      一、HTTP/0.9HTTP是基于TCP/IP协议的应用层协议。它不涉及数据包传输,主要规定了客户端和服务器之间的通信格式,默认使用80端口。最早版本是1991年发布的0.9版。该版本及其简单,只有一个命......
  • Python编程实战营:四款实用小项目助你快速入门,从零开始打造你的个人项目集!
    踏入编程世界的门槛,总是伴随着既兴奋又忐忑的心情。作为Python的新手,你是否渴望通过实际项目来巩固知识、提升技能?本篇文章将引领你踏上一段从理论到实践的精彩旅程,通过四个精心设计的项目,让你在趣味与挑战中快速成长。项目一:简易文本编辑器首先,我们将从基础出发,动手打造一......
  • 如何使用cURL发送POST请求?快速搞定网络请求
    大家好,小编又来分享干货啦!今天我们要聊聊网络开发中常见的一个操作——如何使用cURL发送POST请求。带你快速掌握发送POST请求的技巧,并且还会揭秘如何通过代理IP进一步优化你的网络请求。1.cURL是什么?简单来说,cURL是一个用于向服务器传递数据的命令行工具,它支持多种协议,最常用的就......
  • LLaMA-Factory微调入门个人重制版
    LLaMA-Factory微调入门个人重制版说明:首次发表日期:2024-08-30LLaMA-Factory官方Github仓库:https://github.com/hiyouga/LLaMA-Factory关于本文是对LLaMA-Factory入门教程https://zhuanlan.zhihu.com/p/695287607的个人重制版,记录一下学习过程,省略掉了很多文字部分,建议......