跟隨官網(wǎng)學(xué)nestjs之入門

起步

Nest 是一個(gè)用于構(gòu)建高效,可擴(kuò)展的 Node.js 服務(wù)器端應(yīng)用程序的框架。

安裝

可以使用官方提供的 cli 構(gòu)建項(xiàng)目,也可以克隆啟動(dòng)項(xiàng)目

  • 使用 cli 安裝
 npm i -g @nestjs/cli
 nest new nest-demo

這個(gè)時(shí)候就已經(jīng)初始化好了一個(gè)入門項(xiàng)目,打開項(xiàng)目,可以看到項(xiàng)目基本配置已經(jīng)齊全,包含了 nest 核心文件

src
├── app.controller.ts // 帶有單個(gè)路由的基本控制器示例。
├── app.module.ts // 應(yīng)用程序的根模塊。
└── main.ts // 應(yīng)用程序入口文件。它使用 NestFactory 用來創(chuàng)建 Nest 應(yīng)用實(shí)例。
  • 運(yùn)行項(xiàng)目
yarn start

訪問 http://localhost:3000,就可以看到 Hello World!。最簡單的架子就已經(jīng)好了,現(xiàn)在就是學(xué)習(xí) nest 它提供的方法api

控制器

控制器負(fù)責(zé)處理傳入的 請(qǐng)求 和向客戶端返回 響應(yīng) ,就是我們所說的 controller 層。

路由

nest 實(shí)現(xiàn)路由,需要用到 @Controller() 裝飾器,可以為其設(shè)置前綴,使用路徑前綴可以對(duì)一組相關(guān)的路由進(jìn)行分組,接下來將會(huì)用一個(gè)例子 user 模塊完成

  • 使用 cli 內(nèi)置命令創(chuàng)建一個(gè) controller
nest g controller user

這時(shí)就會(huì)看到項(xiàng)目里已經(jīng)有了 user 文件

import { Controller, Get } from '@nestjs/common';

@Controller('user')
export class UserController {

  @Get()
  findOne(): string {
    return 'this is user controller';
  }
}

其中 @Get() 是一個(gè)裝飾器,告訴我們這個(gè)方法是 HTTPget 請(qǐng)求,那么我們參數(shù)如何傳遞,這時(shí)就需要用到其他的裝飾器。
我們重啟一下服務(wù),在瀏覽器輸入 http://localhost:3000/user,就能看到頁面顯示的 this is user controller。我們也可以使用 yarn start:dev 命令來熱更新,就不用每次更改代碼后重啟服務(wù)。

路由參數(shù)

nest 提供 @Param()@Body() 等等裝飾器,我們改造一下剛剛,提供一個(gè)根據(jù) id 查詢用戶的接口

import { Controller, Get, Param } from '@nestjs/common';

@Controller('user')
export class UserController {

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `find a user by id = ${id}`;
  }
}

在瀏覽器輸入 http://localhost:3000/user/1,就能看到 find a user by id = 1。這就是使用路由 params 參數(shù),當(dāng)然使用 query 也是可以的;我們?cè)黾右粋€(gè)查詢列表的接口

  @Get()
  findAll(
    @Query('pageIndex') pageIndex: number,
    @Query('pageSize') pageSize: number,
  ): string {
    return `find user list, pageIndex = ${pageIndex}, pageSize = ${pageSize}`;
  }

這時(shí)在瀏覽器輸入 http://localhost:3000/user?pageSize=10&pageIndex=1,就會(huì)看見 find user list, pageIndex = 1, pageSize = 10

API 文檔

OpenAPI(Swagger)規(guī)范是一種用于描述 RESTful API 的強(qiáng)大定義格式。 Nest 提供了一個(gè)專用模塊來使用它。

安裝

$ npm install --save @nestjs/swagger swagger-ui-express

引導(dǎo)

安裝過程完成后,打開根目錄文件 main.ts,并使用 SwaggerModule 類初始化 Swagger

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const options = new DocumentBuilder()
    .setTitle('nest demo example')
    .setDescription('The nest demo API description')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, options);

  SwaggerModule.setup('api-docs', app, document);

  await app.listen(3000);
}
bootstrap();

這時(shí)訪問 http://localhost:3000/api-docs/ 就能看見如下的 Swagger UI 界面:

img

我們刪除項(xiàng)目中 app 模塊多余的代碼,為當(dāng)前 user 模塊打上標(biāo)簽

刪除 app.service.ts、app.controller.ts,修改 app.module.ts

import { Module } from '@nestjs/common';
import { UserController } from './user/user.controller';

@Module({
  imports: [],
  controllers: [UserController],
  providers: [],
})
export class AppModule {}

user.controller.ts 加上 ApiTigs 告訴查看文檔的人這個(gè)user模塊,nestjs/swagger 提供了一系列內(nèi)置注解方法,可以給接口添加更多的描述,便于使用者能更好的閱讀理解接口

import { Controller, Get, Param, Query } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';

@ApiTags('user')
@Controller('user')
export class UserController {

  @ApiOperation({
    summary: 'find a user by id',
  })
  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `find a user by id = ${id}`;
  }

  @ApiOperation({
    summary: 'find user list',
  })
  @Get()
  findAll(
    @Query('pageIndex') pageIndex: number,
    @Query('pageSize') pageSize: number,
  ): string {
    return `find user list, pageIndex = ${pageIndex}, pageSize = ${pageSize}`;
  }
}

這時(shí)刷新瀏覽器,就能看到文檔更新了

img

swagger 的好處就是我們可以使用它的 try it out 來模擬真實(shí)發(fā)起請(qǐng)求,也能看到請(qǐng)求需要的參數(shù)以及請(qǐng)求返回的數(shù)據(jù),比 postman 更方便,而且我們還可以寫上參數(shù)示例。

SwaggerModule 在路由處理程序中查找所有使用的 @Body()@Query()@Param() 裝飾器來生成 API 文檔。該模塊利用反射創(chuàng)建相應(yīng)的模型定義,比如我們新增一個(gè)創(chuàng)建用戶的接口

新建一個(gè) dto

import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
  @ApiProperty({ example: 'username' })
  readonly username: string;

  @ApiProperty({ example: 18 })
  readonly age: number;

  @ApiProperty({ example: 1 })
  readonly sex: number;

  @ApiProperty({ example: 'address' })
  readonly address: string;
}

user.controller.ts 新增創(chuàng)建接口

...
  @ApiOperation({
    summary: 'create a user',
  })
  @Post()
  create(
    @Body() createUserDto: CreateUserDto,
  ): string {
    return `create user, the user name = ${createUserDto.username}`;
  }
...

刷新接口文檔,就能看見新增了一個(gè)接口,基于 CreateUserDto 將創(chuàng)建模塊定義:

img

其中我們使用 ApiProperty 裝飾器來說明改字段的屬性,更多用法可以查閱官方文檔

數(shù)據(jù)庫

官方提供幾種數(shù)據(jù)庫模型,這里我使用的是 Sequelize。

Sequelize 是一個(gè)用普通 JavaScript 編寫的流行對(duì)象關(guān)系映射器( ORM ),但是有一個(gè) Sequelize-TypeScript 包裝器,它為基本 Sequelize 提供了一組裝飾器和其他附加功能。

安裝

$ npm install --save sequelize sequelize-typescript mysql2 @nestjs/sequelize
$ npm install --save-dev @types/sequelize

模型注入

Sequelize 中,模型在數(shù)據(jù)庫中定義了一個(gè)表。該類的實(shí)例表示數(shù)據(jù)庫行。首先,我們至少需要一個(gè)實(shí)體 user.model.ts

import {
  Column,
  Model,
  Table,
  TableOptions,
  DataType,
  CreatedAt,
  UpdatedAt,
} from 'sequelize-typescript';

const tableOptions: TableOptions = {
  tableName: 'user',
};

@Table(tableOptions)
export class User extends Model<User> {
  @Column({
    type: DataType.BIGINT,
    allowNull: false,
    autoIncrement: true,
    primaryKey: true,
  })
  public id: number;

  @Column({
    type: DataType.STRING,
    allowNull: false,
  })
  username: string;

  @Column({
    type: DataType.STRING,
    allowNull: true,
  })
  password: string;

  @Column({
    type: DataType.STRING,
    allowNull: false,
  })
  address: string;

  @Column({
    type: DataType.BIGINT,
    allowNull: false,
  })
  age: number;

  @Column({
    type: DataType.BIGINT,
    allowNull: false,
  })
  sex: number;

  @CreatedAt public createdAt: Date;

  @UpdatedAt public updatedAt: Date;
}

該實(shí)體的定義,對(duì)應(yīng)的就是映射到數(shù)據(jù)庫的模型。新建 user.module.ts 來注入該模型

import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { UserController } from './user.controller';
import { User } from './user.model';

@Module({
  imports: [
    SequelizeModule.forFeature([User]),
  ],
  providers: [],
  controllers: [UserController],
  exports: [],
})
export class UserModule {}

連接數(shù)據(jù)庫

我們?cè)诟?jié)點(diǎn)實(shí)例化,并連接數(shù)據(jù)庫

import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { UserModule } from './user/user.module';
import { User } from './user/user.model';
import { UserController } from './user/user.controller';

@Module({
  imports: [
    UserModule,
    SequelizeModule.forRoot({
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'nest_demo',
      models: [User],
    }),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

我們創(chuàng)建服務(wù)層,新建 user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { User } from './user.model';
import { CreateUserDto } from './dto';

@Injectable()
export class UserService {

  constructor(
    @InjectModel(User)
    private userModel: typeof User,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = new User();
    user.username = createUserDto.username;
    user.age = createUserDto.age;
    user.address = createUserDto.address;
    user.sex = createUserDto.sex;

    return await user.save();
  }

  async findAll(): Promise<User[]> {
    return await this.userModel.findAll<User>({
      attributes: ['id', 'username', 'address', 'age', 'sex'],
    });
  }

  async findOneById(id: string): Promise<User> {
    return await this.userModel.findOne<User>({
      where: { id },
      attributes: ['id', 'username', 'address', 'age', 'sex'],
    });
  }
}

然后修改 controller

import { Controller, Get, Param, Query, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { CreateUserDto } from './dto';
import { UserService } from './user.service';
import { User } from './user.model';

@ApiTags('user')
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @ApiOperation({
    summary: 'find a user by id',
  })
  @Get(':id')
  findOne(@Param('id') id: string): Promise<User> {
    return this.userService.findOneById(id);
  }

  @ApiOperation({
    summary: 'find user list',
  })
  @Get()
  findAll(): Promise<User[]> {
    return this.userService.findAll();
  }

  @ApiOperation({
    summary: 'create a user',
  })
  @Post()
  create(
    @Body() createUserDto: CreateUserDto,
  ): Promise<User> {
    return this.userService.create(createUserDto);
  }
}

這時(shí)訪問文檔,我們執(zhí)行一下查詢接口,就可以看到返回的數(shù)據(jù)結(jié)果了

img

到此,Nest 入門已經(jīng)結(jié)束了,接下來我們會(huì)介紹簡單的守衛(wèi)

代碼傳送門:nest-demo

參考資料:Nest 文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容