2020-02-15 5 技术
nestjs快速开发项目

整合了nestjs项目通用架构、常用功能集成的一个快速开发项目。

项目地址:https://github.com/YES-Lee/nest-seed-proj

集成功能

  • ORM: sequelize
  • API: restful api, graphql
  • 认证:passport
  • 文档:swagger
  • 安全:helmet
  • 日志:nest-pino

功能不断完善,将来或提供可插拔的开发体验

快速开始

# 拉取代码
git clone https://github.com/YES-Lee/nest-seed-proj.git

cd nest-seed-proj
yarn install # or npm intall
yarn start:dev # or npm run start:dev

项目结构

src
├─ config // 配置模块
├─ database // 数据库模块
│  └─ models // 存放sequelize/graphql数据库模型
├─ decorators // 自定义装饰器目录
├─ dto // restful 接口模型目录
├─ graphql // graphql 模块
├─ enums // 枚举目录
├─ filters // error filters
├─ interceptors // interceptors
├─ pipes // pipes
└─ modules // 业务逻辑模块目录,所有业务相关逻辑都放到该目录对应的模块

接口格式

{
  error_code: number; // 错误码,只在异常时返回
  error_message: string; // 错误信息,只在异常时返回
  data: any;
  timestamp: any; // 时间戳
  path: string; // 请求路径
}

dtc/support中提供了ApiResponse类,提供了success, error两个静态方法。

Config模块

Config模块是提供项目配置的模块,提供全局的项目配置,以依赖注入的方式使用配置项。src/config目录下,配置文件以config.{env}.ts命名,config.default.ts作为项目的默认配置,默认都会加载,其它配置会自动和config.default.ts合并。

使用Config

Config模块是全局模块,可以通过注入的方式使用,如在数据库模块中使用配置

{
  provide: 'SEQUELIZE',
  inject: [ConfigService, Logger],
  useFactory: (configService: ConfigService, logger: Logger) => {
    const sequelize = new Sequelize({
      dialect: configService.get('database.dialect'),      host: configService.get('database.host'),      port: configService.get('database.port'),      username: configService.get('database.username'),      password: configService.get('database.password'),      database: configService.get('database.database'),      // ...
    });
    return sequelize;
  }
}

日志

项目使用了nest-pino作为日志工具,提供了友好地pretty等功能,可以直接在config中配置日志级别、输出路径等。

// main.module.ts
LoggerModule.forRootAsync({
  inject: [ConfigService],
  useFactory: (configService: ConfigService) => {
    return {
      enabled: configService.get('log.enabled'),
      timestamp: configService.get('log.timestamp'),
      level: configService.get('log.level'),
      useLevelLabels: configService.get('log.useLevelLabels'),
      prettyPrint: configService.get('log.prettyPrint'),
      stream: pino.destination(configService.get('log.path')),
    };
  }
})

文档

项目集成了swagger文档工具,项目启动后访问http://localhost:3001/swagger访问,swagger用法参考官方文档

Graphql

Graphql模块使用code first方式开发,所有的接口类型定义在src/graphql/schemas目录下,graphql/schemas/support中也提供了全局统一的类型处理如分页接口等抽象类。

请求参数

请求参数分为两种类型,一种为args,另一种为input,区别在于两种方式在请求的时候传输的参数不同,args类型里的字段会展开为resolver方法的参数列表,而input类型定义的是resolver中的一个参数,如下面的列子:(文档(https://typegraphql.ml/docs/faq.html#is-inputtype-different-from-argstype)

  • args类型

    @ArgsType()
    class UserListArgs {
    @Field(type => Int)
    page: number;
    @Field(type => Int)
    pageSize: number;
    }
    
    @Query()
    userList(@Args() args: UserListArgs) {}
    
    // 实际请求时传输的参数是
    
    {
    page: 1,
    pageSize: 10
    }
  • input类型

    @ArgsType()
    class UserListArgs {
      @Field(type => Int)
      page: number;
      @Field(type => Int)
      pageSize: number;
    }
    
    @Query()
    userList(@Args('userListArgs') args: UserListArgs) {}
    
    // 实际请求时传输的参数是
    
    {
      userListArgs: {
        page: 1,
        pageSize: 10
      }
    }

响应

响应类型统一为ObjectTypesupport中定义了分页接口统一返回格式IPageResult,由于graphql不具有泛型,因此在实现IPageResult时,必须显示指定rows的类型,如:

@ObjectType({
  implements: IPageResult,
  description: '用户列表'
})
export class UserListResult implements IPageResult<User> {
  count: number;
  @Field(type => [User])
  rows: User[];
}

Playground

启动项目后访问http://localhost:3001/graphqlplayground是目前主流的graphql文档和测试工具。

相关技术