Skip to content

Latest commit

 

History

History
198 lines (153 loc) · 5.37 KB

20.精读《Nestjs》文档.md

File metadata and controls

198 lines (153 loc) · 5.37 KB

020.精读《Nestjs》文档.md

nodejs mvc 优雅设计(TS 结合 Nodejs 结合最好的例子)

  • Nestjs - 基于 Express socket.io 封装的 nodejs 框架
  • 亮点: Modules, Controllers, Components
    • Modules 最大颗粒度的拆分,表示应用或模块
    • Controllers 传统意义的控制器,一个 Modules 拥有多个 Controller
    • Components 用于做 Services, 比如将数据库 CURD 封装在 Services 中,每个 Service 就是一个 Component

装饰器路由

  • 路由放置函数头上,做到路由去中心化

    @Controller()
    export class UsersController {
    	@Get('users')
    	getAllUsers() {}
    	
    	@Get('users/:id')
    	getUser() {}
    	
    	@Post('users')
    	addUser() {}
    }
    

    模块间依赖注入

    Modules, Controllers, Components 之间通过依赖注入相互关联, 通过装饰器声明

    @Component() 
    export class UserService {
    	getAllUsers() {
    	return []
    	}
    }
    @Module({
        controllers: [UsersController],
        components: [UserService]
    })
    export class ApplicationModule {}
    • AppliactionModule 申明其内部 Controllers 和 Components 后,就可以在 Controller 中注入 Components 了

      @Controller
      export class UsersController {
          	constructor(private userService: UserService) {}
      	@Get('users')
          getAllUsers() {
              return this.userService.getAllUsers()
          }
      }

      装饰器参数

      • NestJs 通过装饰器获取请求参数

        @Get('/:id')
        public async getUser(
        	@Response() res,
            @Param('id') id,
        ) {
            const user = await this.userService.getUser(id);
            res.status(HTTPStatus.OK).json(user)
        }
        // @Response 获取res @Param获取路由参数, @Query获取 url query参数, @Body 获取HTTP body参数

Typeorm 反射配合 TS

  • 定义实体(每个实体对应数据库一张表,Typeorm 每次启动都会同步表结构到数据库,完全不用食用数据库查看表结构, 所有结构定义全在代码中)

    @Entity()
    	export class Card {
            @PrimaryGeneratedColumn({ // 定义主键列 
                comment: '主键',
            })
            id: number, // 自动识别id为数字类型
        
    @Column({  // 列定义唯一信息长度等
        comment: '名称',
        length: 30,
        unique: true
    })
    name: string = 'nick' // 自动识别name为string类型,且赋初始值nick
    }
    • 通过@Entity 将类定义为实体

    自动校验

    • 只有判断参数类型不够,还可以用class-validator 做所有形式的校验

      @column({
          comment: '配置JSON',
          length: 5000,
      })
      @Validator.IsString({message: '必须为字符串'})
      @Validator.length(0, 5000, {message: '长度在0~5000'})
      content: string;
  • 新增实体时,需要校验所有字段,但是更新实体,由于性能需要不会一次查询所有字段,指定更新时,不校验没有赋值的字段,通过 Typeorm 的EventSubscriber完成数据库操作前的代码校验。控制新增时的全字段校验,删除不做校验

    @EventSubcriber() 
    export class EverythingSubcriber implement EntitySubscriberInterface<any> {
        async beforeInsert(event: InsertEvent<any>) {
            const validateErrors = await validate(event.entity);
            if(validateErrors.length > 0) {
                throw new HttpException(getErrorMessage(validateErrors), 404);
            }
        }
        async beforeUpdate(event: UpdateEvent<any>) {
            const validateErrors = await validate(event.entity, {
                skipMissingProperties: true,
            });
            if(validateErrors.length > 0) 
            throw new HttpException(getErrorMessage(validateErrors), 404)
        }
    }
    • HttpException 校验失败会终止执行,立即返回服务给客户端
const card = await this.cardService.add(name, description);
  // 如果传入参数实体校验失败,会立刻返回失败,并提示 `@Validator.IsString({ message: '必须为字符串' })` 注册时的提示信息
  // 如果插入失败,也会立刻返回失败
  // 所以只需要处理正确情况
  res.status(HttpStatus.OK).json(card);

外键

  • @OneToOne @OneToMany @ManyToOne @ManyToMany
@Entity() 
export class Comment {
    @PrimaryGeneratedColumn({
        comment: '主键',
    })
    id: number;
@ManyToOne(type => User, user => Comments) // 关联关系
@JoinColumn()
user: User;
}
  • 使用 typeorm 查询 User 时,自动外键查询到关联的评论, 保存在 user.comments 中, 查询 Comment 时,会自动查询到关联的 User,保存在 comment.user 中

部署

  • 使用 Docker 部署 Mysql + NodeJs,通过 docker-compose 将数据库和服务跑在 docker 上,内部通信
  • nodejs 服务运行时,要等待数据库服务启动完毕,有一个启动等待的需求,通过environment 拓展功能 docker-compose.yml
version: "2"
services:
  app:
    build: ./
    restart: always
    ports:
      - "5000:8000"
    links:
      - db
      - redis
    depends_on:
      - db
      - redis
    environment:
      WAIT_HOSTS: db:3306 redis:6379

summary

  • nodejs 中间件实现的很精妙, 与 Modules 完美结合
  • -如果前端可以设计 ie11,推荐纯 Proxy 实现的 dob, 配合 react 效率高