赞
踩
本文章记录自己学习
Nest
的过程,适于前端及对后端没有基础但对Nest
感兴趣的同学,如有错误,欢迎各位大佬指正
controllers
、services
、modules
、dto
、entities
这几个重要目录Nest
以及其swagger
文档的使用,那么本篇便开始讲述Nest
如何连接数据库、在创建实体中使用Pipe
管道进行参数校验ORM
(对象关系映射器),如TypeORM
、Sequelize
,在此我选择TypeORM
,它本身是由TypeScript
写的,对Nest
的支持也会好点,而且Nest
也提供了开箱即用的@nestjs/typeorm
包npm install --save @nestjs/typeorm
TypeORM
连接mysql
数据库,那么我们需要安装依赖npm install --save typeorm mysql2
nest
带有环境配置yarn add @nestjs/config
@nestjs/config
默认会从项目根目录载入并解析.env
文件,从.env
文件和process.env
合并环境变量键值对,forRoot()
方法注册了 ConfigService
提供者,后者提供了一个 get()
方法来读取这些解析/合并的配置变量。要注入ConfigService
,需要在需要使用的地方先导入ConfigModule
。app.module
中使用了ConfigModule.forRoot()
,将isGlobal
设置为true
,在其他地方使用时不需要做任何事,表示全局使用,此时就可以在全局范围内使用process.env.xxx
读取全局变量app.module.ts
文件中使用@nestjs/config
进行全局配置,以及使用@nestjs/typeorm
提供的TypeOrmModule
连接数据库如下// app.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; // 环境配置相关 import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), TypeOrmModule.forRootAsync({ useFactory: () => ({ type: 'mysql', host: process.env.DATABASE_HOST, port: +process.env.DATABASE_PORT, // 来自process.env的每个值都是字符串,前面加+转数字 username: process.env.DATABASE_USER, password: process.env.DATABASE_PASSWORD, database: process.env.DATABASE_NAME, autoLoadEntities: true, // 自动加载模块 推荐 // entities: [path.join(__dirname, '/../**/*.entity{.ts,.js}')], // 不推荐 synchronize: true // 开启同步,生产中要禁止 }) }), ], controllers: [], providers: [] }) export class AppModule {}
forRootAsync
使用了TypeORM
的异步工程模式,这样可以解决imports
的顺序问题,也就是说,使用了forRootAsync
,可以不用在意imports
这个数组中使用TypeOrmModule
的顺序,可以任意放,不用在意其他模块引入的顺序TypeORM
提供了autoLoadEntities
来自动加载创建的数据库实体,使用这种方式也比较推荐,也可以使用entities: [path.join(__dirname, '/../**/*.entity{.ts,.js}')]
的方式,这表示,指定包含所有实体的整个目录,该目录下所有实体都将被加载.env
如下DATABASE_USER=root
DATABASE_PASSWORD=root
DATABASE_NAME=nest-series
DATABASE_PORT=3306
DATABASE_HOST=localhost
SERVICE_PORT=1231
TypeORM
user
用户实体,创建src/entities/user.entity.ts
文件
Role
是角色实体// user.entity.ts import { Entity, Column, JoinTable, ManyToMany, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; import { Role } from './role.entity'; // @Entity()装饰器自动从所有类生成一个SQL表,以及他们包含的元数据 // @Entity('users') // sql表名为users @Entity() // sql表名为user export class User { // 主键装饰器,也会进行自增 @PrimaryGeneratedColumn() id: number; // 列装饰器 @Column() username: string; // @Column('json', { nullable: true }) json格式且可为空 @Column() password: string; // 定义与其他表的关系 // name 用于指定创中间表的表名 @JoinTable({ name: 'user_roles' }) // 指定多对多关系 /** * 关系类型,返回相关实体引用 * cascade: true,插入和更新启用级联,也可设置为仅插入或仅更新 * ['insert'] */ @ManyToMany((type) => Role, (role) => role.users, { cascade: true }) roles: Role[]; @CreateDateColumn() createAt: Date; @UpdateDateColumn() updateAt: Date; }
src/entities/role.entity.ts
// role.entity.ts import { Entity, Column, ManyToMany, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; import { User } from './user.entity'; @Entity() export class Role { @PrimaryGeneratedColumn() id: number; @Column() name: string; @ManyToMany((type) => User, (user) => user.roles) users: User[]; @CreateDateColumn() createAt: Date; @UpdateDateColumn() updateAt: Date; }
@Entity()
来标记通过定义一个新类来创建的实体,装饰器自动从所有类生成一个SQL表,以及他们包含的元数据。@Colimn()
来标记普通列@Column("int")
/@Column({ type: "int" })
:数字类型@Column("varchar", { length: 200 })
:字符串类型,长度为200@Column({ type: "int", length: 200 })
:数字类型,长度为200@Column()
上指定列选项:
title: string
: 数据库表中的列名。unique: true
:将列标记为唯一列,里面的值不可重复nullable: boolean
: 在数据库中使列NULL
或NOT NULL
。 默认情况下,列是nullable:false
。@Column({
type: "varchar",
length: 150,
unique: true,
// ...
})
title: string;
@PrimaryColumn()
来标记主列,需要给它手动分配值@PrimaryGeneratedColumn()
来标记主列,该值将使用自动增量值自动生成@PrimaryGeneratedColumn('uuid')
来标记主列,该值将使用uuid
(通用唯一标识符)自动生成,uuid
可以被认为是唯一的
uuid
是让分布式系统中的所有元素都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的uuid
。在这样的情况下,就不需考虑数据库创建时的名称重复问题uuid
的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12
的32
个字符,如:550e8400-e29b-41d4-a716-446655440000
。@ManyToMany()
指明多对多关系,多对多是一种A
包含多个B
实例,而B
包含多个A
实例的关系。比如:在一个系统中会有用户和角色,用户是会有很多个的,角色也是有很多个的,当然会有一个人是产线的研发Leader
,他也可以是一个横向团队的Leader
。@JoinTable()
是@ManyToMany()
关系所必需的。cascade
@ManyToMany((type) => Role, (role) => role.users, { cascade: true })
relations
表示需要加载主体,如下是一个查询用户列表所有数据的代码
relations: { roles: true }
relations: ['roles']
async getUserList() {
return await this.userRepository.find({
// 1
// relations: {
// roles: true
// },
// 2
relations: ['roles'],
});
}
@JoinTable()
用于多对多关系,使用后会由TypeORM
自动创建一个单独表,这张表的默认名字为这两张关系表表名以下划线_
连接的名字,也可以自定义这张表名,设置方法如上代码@JoinTable()
。 @JoinTable()
必须只在关系的一边(@JoinTable()
在user
一边,而role
则没有)CreateDateColumn
:自动为实体插入日期UpdateDateColumn
:在实体每次更新时,自动更新实体日期Navicat Premium
,我这使用的是vscode
的一款插件Controller
里做,但是这种验证逻辑是通用的,每个Controller
里都做一遍太麻烦了,所以能不能在Controller
之前就做好呢,这里我们可以使用Nest
提供的管道Pipe
。管道有两个典型的应用场景:
参数(arguments)
会由 控制器(controllers)的路由处理程序 进行处理。Nest 会在调用这个方法之前插入一个管道,管道会先拦截方法的调用参数,进行转换或是验证处理,然后用转换好或是验证好的参数调用原方法。Nest
自带九个开箱即用的管道,即
ValidationPipe
ParseIntPipe
ParseFloatPipe
ParseBoolPipe
ParseArrayPipe
ParseUUIDPipe
ParseEnumPipe
DefaultValuePipe
ParseFilePipe
他们从 @nestjs/common
包中导出。
npm i --save class-validator class-transformer
dto
中添加装饰器了
IsString
:检查值是否为字符串IsNotEmpty
:检查值是否不为空装饰器 | 描述 |
---|---|
@IsBoolean() | 是否为布尔值 |
@IsString() | 是否为字符串 |
@IsInt() | 是否为整数 |
@IsArray() | 是否为数组 |
@IsNotEmpty() | 检查给定值是否不为空 |
// create-user.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsString, IsNotEmpty } from 'class-validator'; export class CreateUserDto { //ApiProperty是对数据类型的描述 @ApiProperty({ description: '用户名', default: 'Kylin' }) @IsNotEmpty({ message: '用户名不为空' }) @IsString() username: string; @ApiProperty({ description: '密码', default: 'siJue' }) @IsNotEmpty({ message: '密码不为空' }) @IsString() password: string; @ApiProperty({ description: '角色', default: ['admin'] }) @IsNotEmpty() @IsString({ each: true }) roles: string[]; }
main.ts
中使用全局管道
app.useGlobalPipes(new ValidationPipe());
全局应用管道 对输入数据进行转换或者验证import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { AppModule } from '@/modules/app.module'; import { generateDocument } from './swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 设置全局路由前缀 app.setGlobalPrefix('api'); // 全局应用管道 对输入数据进行转换或者验证 app.useGlobalPipes(new ValidationPipe()); // 创建swagger文档 generateDocument(app); await app.listen(+process.env.SERVICE_PORT, () => { console.log(`项目运行在http:localhost:${process.env.SERVICE_PORT}/api`); }); } bootstrap();
TypeORM
操作数据库以及使用Pipe
管道进行参数校验Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。