环境配置
参考文档地址
在 Node.js 应用程序中,通常使用.env文件来表示每个环境,这些文件包含键值对,每个键代表一个特定值。在不同的环境中运行应用程序只需交换正确的文件即可.env。
安装所需的依赖项
yarn add @nestjs/config -D
@@filename(app.module)
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
上述代码将从.env
默认位置(项目根目录)加载并解析文件
示例.env文件如下所示:
DATABASE_USER=test
DATABASE_PASSWORD=test
自定义环境文件
默认情况下,包会.env在应用程序的根目录中查找文件。要为文件指定其他路径.env,请设置envFilePath传递给 的(可选)选项对象的属性forRoot(),如下所示:
ConfigModule.forRoot({
envFilePath: '.development.env',
});
可以为.env文件指定多个路径,如下所示:
ConfigModule.forRoot({
envFilePath: ['.env.development.local', '.env.development'],
});
Tip: 如果在多个文件中发现一个变量,则第一个变量优先。
使用模块
ConfigModule.forRoot({
// 声明为全局模块,在其他模块中可以直接使用无需引入了
isGlobal: true,
});
自定义配置文件
自定义配置文件来返回嵌套的配置对象
@@filename(config/configuration.ts)
export default () => ({
port: parseInt(process.env.PORT, 10) || 3000,
database: {
host: process.env.DATABASE_HOST,
port: parseInt(process.env.DATABASE_PORT, 10) || 5432
}
});
使用传递给方法的选项对象的属性load
来加载此文件
import configuration from './config/configuration';
@Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
],
})
export class AppModule {}
使用
ConfigService
引入ConfigModule
@@filename(feature.module.ts)
@Module({
imports: [ConfigModule],
// ...
})
使用标准构造函数注入来注入
constructor(private configService: ConfigService) {}
使用
// 以环境方式获取
const dbUser = this.configService.get<string>('DATABASE_USER');
// 自定义配置文件方式获取
const dbHost = this.configService.get<string>('database.host');
配置 Typeorm + Mysql
# 安装typeorm 和 mysql
yarn add typeorm mysql2
yarn add -D @nestjs/typeorm
安装过程完成后,我们可以将其导入TypeOrmModule根目录AppModule
@@filename(app.module)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123456',
database: 'test_project',
entities: [ 'dist/**/*.entity{.ts,.js}' ],
// 重试连接的延迟时间(ms)默认值:3000
retryDelay: 500,
// 重试连接次数默认值:10
retryAttempts: 10,
// 如果为true,实体将自动加载
autoLoadEntities: true,
// 是否应在每次启动应用程序时自动创建数据库架构。请谨慎使用此选项,不要在生产环境中使用 - 否则可能会丢失生产数据
synchronize: true,
}),
],
})
export class AppModule {}
配合ConfigModule
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { TypeOrmModule } from '@nestjs/typeorm'
import { ConfigModule, ConfigService } from '@nestjs/config'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env'
}),
TypeOrmModule.forRootAsync({
// @ts-ignore
useFactory: async (config: ConfigService) => ({
type: config.get('DATABASE_TYPE'),
host: config.get('DATABASE_HOST'),
port: config.get('DATABASE_PORT'),
username: config.get('DATABASE_USERNAME'),
password: config.get('DATABASE_PASSWORD'),
database: config.get('DATABASE_NAME'),
entities: [ __dirname + '/**/*.entity{.ts,.js}' ],
synchronize: true,
retryDelay: 1000,
retryAttempts: 10
}),
inject: [ ConfigService ],
})
],
controllers: [ AppController ],
providers: [ AppService ],
})
export class AppModule {}
验证器
yarn add class-validator class-transformer
自定义验证规则管道
创建文件
nest g pi pipe/validation-pipe
@@filename(pipe/validation-pipe.pipe)
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common'
import { plainToInstance } from 'class-transformer'
import { validate } from 'class-validator'
@Injectable()
export class ValidationPipe implements PipeTransform {
async transform(value: any, metadata : ArgumentMetadata) {
const metaType = metadata.metatype
if (!metaType || !this.toValidate(metaType)) {
// 如果没有传入验证规则,则不验证,直接返回数据
return value
}
// 将对象转换为 Class 来验证
const object = plainToInstance(metaType, value)
const errors = await validate(object)
if (errors.length > 0) {
// 取第一个错误信息并返回
const msg = Object.values(errors[0].constraints)[0]
throw new BadRequestException(msg)
}
return value
}
private toValidate(metaType: unknown): boolean {
const types: unknown[] = [ String, Boolean, Number, Array, Object ]
return !types.includes(metaType)
}
}
引入管道
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ValidationPipe } from '../pipe/validation-pipe/validation-pipe.pipe'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe())
await app.listen(3000)
}
bootstrap()
验证
@@filename(user/user.module.ts)
...
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto)
}
...
@@filename(user/dto/create-user.dto.ts)
import { IsEmail, IsNotEmpty } from 'class-validator'
export class CreateUserDto {
@IsEmail({
message: '邮箱格式不正确'
})
email: string;
@IsNotEmpty({
message: '密码不能为空'
})
password: string;
}
拓展
在Param
参数中使用
...
@Get(':id')
findOne(@Param() params: FindOneParams) {
return 'This action returns a user';
}
...
--------------------------------------------------
import { IsNumberString } from 'class-validator';
export class FindOneParams {
@IsNumberString()
id: number;
}
禁用详细错误
app.useGlobalPipes(
new ValidationPipe({
disableErrorMessages: true,
})
)
白名单
app.useGlobalPipes(
new ValidationPipe({
// 删除掉未在dto管道中声明的属性
whitelist: true,
// 返回参数异常错误响应
forbidNonWhitelisted: true
})
)
类型转换
默认情况下,每个路径参数和查询参数都以String的类型传输
# 局部使用
@@filename(cats.controller.ts)
@Post()
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
# 全局配置
app.useGlobalPipes(
new ValidationPipe({
transform: true,
})
)
----------
# 在局部使用或者全局配置后,参数将会进行类型转换
@Get(':id')
findOne(@Param('id') id: number) {
console.log(typeof id === 'number'); // true
return 'This action returns a user';
}
明确值转换
ParseIntPipe
| ParseBoolPipe
| ParseStringPipe
import { ParseIntPipe, ParseBoolPipe } from '@nestjs/common'
...
@Get(':id')
findOne(
@Param('id', ParseIntPipe) id: number,
@Query('sort', ParseBoolPipe) sort: boolean,
) {
console.log(typeof id === 'number'); // true
console.log(typeof sort === 'boolean'); // true
return 'This action returns a user';
}
...
映射类型(构建输入验证类型)
PartialType()
将所有的参数设置为可选
import { PartialType } from '@nestjs/mapped-types'
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
export class UpdateCatDto extends PartialType(CreateCatDto) {}
// UpdateCatDto:
// name?: string;
// age?: number;
// breed?: string;
PickType()
函数通过从输入类型中选择一组属性来构造新类型(类)
import { PickType } from '@nestjs/mapped-types'
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
export class UpdateCatAgeDto extends PickType(CreateCatDto, ['age'] as const) {}
OmitType()
函数通过从输入类型中选取所有属性,然后删除一组特定的键来构造类型
import { OmitType } from '@nestjs/mapped-types'
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
export class UpdateCatDto extends OmitType(CreateCatDto, ['name'] as const) {}
IntersectionType()
函数将两种类型组合成一种新类型(类)
import { IntersectionType } from '@nestjs/mapped-types'
export class CreateCatDto {
name: string;
breed: string;
}
export class AdditionalCatInfo {
color: string;
}
export class UpdateCatDto extends IntersectionType(
CreateCatDto,
AdditionalCatInfo,
) {}
类型映射实用函数是可组合的
export class UpdateCatDto extends PartialType(
OmitType(CreateCatDto, ['name'] as const),
) {}
验证数组
@Post()
createBulk(
@Body(new ParseArrayPipe({ items: CreateUserDto }))
createUserDtos: CreateUserDto[],
) {
return 'This action adds new users';
}
当参数格式为GET /?ids=1,2,3
时,进行验证并处理
@Get()
findByIds(
@Query('ids', new ParseArrayPipe({ items: Number, separator: ',' }))
ids: number[],
) {
return 'This action returns users by ids';
}