起步
Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。
安装
可以使用官方提供的 cli
构建项目,也可以克隆启动项目
- 使用
cli
安装
npm i -g @nestjs/cli
nest new nest-demo
这个时候就已经初始化好了一个入门项目,打开项目,可以看到项目基本配置已经齐全,包含了 nest
核心文件
src
├── app.controller.ts // 带有单个路由的基本控制器示例。
├── app.module.ts // 应用程序的根模块。
└── main.ts // 应用程序入口文件。它使用 NestFactory 用来创建 Nest 应用实例。
- 运行项目
yarn start
访问 http://localhost:3000
,就可以看到 Hello World!
。最简单的架子就已经好了,现在就是学习 nest
它提供的方法api
控制器
控制器负责处理传入的 请求 和向客户端返回 响应 ,就是我们所说的 controller
层。
路由
在 nest
实现路由,需要用到 @Controller()
装饰器,可以为其设置前缀,使用路径前缀可以对一组相关的路由进行分组,接下来将会用一个例子 user
模块完成
- 使用
cli
内置命令创建一个controller
nest g controller user
这时就会看到项目里已经有了 user
文件
import { Controller, Get } from '@nestjs/common';
@Controller('user')
export class UserController {
@Get()
findOne(): string {
return 'this is user controller';
}
}
其中 @Get()
是一个装饰器,告诉我们这个方法是 HTTP
的 get
请求,那么我们参数如何传递,这时就需要用到其他的装饰器。
我们重启一下服务,在浏览器输入 http://localhost:3000/user
,就能看到页面显示的 this is user controller
。我们也可以使用 yarn start:dev
命令来热更新,就不用每次更改代码后重启服务。
路由参数
nest
提供 @Param()
、@Body()
等等装饰器,我们改造一下刚刚,提供一个根据 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
参数,当然使用 query
也是可以的;我们增加一个查询列表的接口
@Get()
findAll(
@Query('pageIndex') pageIndex: number,
@Query('pageSize') pageSize: number,
): string {
return `find user list, pageIndex = ${pageIndex}, pageSize = ${pageSize}`;
}
这时在浏览器输入 http://localhost:3000/user?pageSize=10&pageIndex=1
,就会看见 find user list, pageIndex = 1, pageSize = 10
API 文档
OpenAPI(Swagger)规范是一种用于描述 RESTful API
的强大定义格式。 Nest
提供了一个专用模块来使用它。
安装
$ npm install --save @nestjs/swagger swagger-ui-express
引导
安装过程完成后,打开根目录文件 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();
这时访问 http://localhost:3000/api-docs/
就能看见如下的 Swagger UI
界面:
我们删除项目中 app
模块多余的代码,为当前 user
模块打上标签
删除 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
告诉查看文档的人这个user模块,nestjs/swagger
提供了一系列内置注解方法,可以给接口添加更多的描述,便于使用者能更好的阅读理解接口
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}`;
}
}
这时刷新浏览器,就能看到文档更新了
swagger
的好处就是我们可以使用它的 try it out
来模拟真实发起请求,也能看到请求需要的参数以及请求返回的数据,比 postman
更方便,而且我们还可以写上参数示例。
SwaggerModule
在路由处理程序中查找所有使用的 @Body()
, @Query()
和 @Param()
装饰器来生成 API
文档。该模块利用反射创建相应的模型定义,比如我们新增一个创建用户的接口
新建一个 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
新增创建接口
...
@ApiOperation({
summary: 'create a user',
})
@Post()
create(
@Body() createUserDto: CreateUserDto,
): string {
return `create user, the user name = ${createUserDto.username}`;
}
...
刷新接口文档,就能看见新增了一个接口,基于 CreateUserDto
将创建模块定义:
其中我们使用 ApiProperty
装饰器来说明改字段的属性,更多用法可以查阅官方文档
数据库
官方提供几种数据库模型,这里我使用的是 Sequelize
。
Sequelize
是一个用普通 JavaScript
编写的流行对象关系映射器( ORM
),但是有一个 Sequelize-TypeScript
包装器,它为基本 Sequelize
提供了一组装饰器和其他附加功能。
安装
$ npm install --save sequelize sequelize-typescript mysql2 @nestjs/sequelize
$ npm install --save-dev @types/sequelize
模型注入
在 Sequelize
中,模型在数据库中定义了一个表。该类的实例表示数据库行。首先,我们至少需要一个实体 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;
}
该实体的定义,对应的就是映射到数据库的模型。新建 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 {}
连接数据库
我们在根节点实例化,并连接数据库
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 {}
我们创建服务层,新建 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);
}
}
这时访问文档,我们执行一下查询接口,就可以看到返回的数据结果了
到此,Nest
入门已经结束了,接下来我们会介绍简单的守卫
代码传送门:nest-demo
参考资料:Nest 文档