前言
前面了解了那么多的相关概念,是时候实践一下了。从新建一个初始化项目框架模版开始实践之前学到的理论知识,从实践的角度把涉及到的知识概念整体串联一下,构建一个多环境开发模版。
起步初始化一个项目
首先确定下node
开发环境,这里大家自行安装检查。安装全局nest
的cli
工具用来初始化一个项目
安装cli
npm i -g @nestjs/cli
cli创建项目
nest new project-name
.
├── README.md
├── nest-cli.json
├── package.json
├── pnpm-lock.yaml
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json
配置多环境开发
首先安装配置依赖包@nestjs/config
npm i --save @nestjs/config
@nestjs/config
内部使用 dotenv 实现。
配置
使用多配置需要导入ConfigModule模块,一般会在根模块AppModal中导入,并使用.forRoot()静态方法导入它的配置
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
使用env方式配置
以上配置会默认载入并解析一个.env文件,从.env文件和process.env合并环境变量键值对,并将结果存储到一个可以通过ConfigService
访问的私有结构。forRoot()
方法注册了ConfigService
提供者,后者提供了一个get()
方法来读取这些解析/合并的配置变量。由于@nestjs/config
依赖dotenv,它使用该包的规则来处理冲突的环境变量名称。当一个键同时作为环境变量(例如,通过操作系统终端如export DATABASE_USER=test
导出)存在于运行环境中以及.env
文件中时,以运行环境变量优先。
- .env
DATABASE_USER=root
DATABASE_PASSWORD=test
- 自定义env文件路径
默认情况下,程序在应用程序的根目录中查找.env
文件。 要为.env
文件指定另一个路径,请配置forRoot()
的配置对象 envFilePath 属性(可选)
ConfigModule.forRoot({
envFilePath: '.development.env',
});
也可以指定多个文件路径
ConfigModule.forRoot({
envFilePath: ['.env.development.local', '.env.development'],
});
如果在多个文件中发现同一个变量,则第一个变量优先。
- 禁止加载环境变量
ConfigModule.forRoot({
ignoreEnvFile: true,
});
- 全局使用配置
当您想在其他模块中使用ConfigModule
时,需要将其导入(这是任何 Nest 模块的标准配置)。 或者,通过将options
对象的isGlobal
属性设置为true
,将其声明为全局模块,如下所示。 在这种情况下,将ConfigModule
加载到根模块(例如AppModule
)后,您无需在其他模块中导入它。
ConfigModule.forRoot({
isGlobal: true,
});
env的环境配置的好处就是简单清晰,而且支持自动载入配置,但是如果配置有很多并且有嵌套的话,这个方式就不太好了,接下来介绍yaml
格式的配置
使用yaml
方式配置
安装js-yaml
包和类型
npm i js-yaml -S
npm i @types/js-yaml -D
在src
文件夹下创建一个config
目录来放各个环境的配置,这里创建3
个环境dev/prod/test
── src
│ ├── app.module.ts
│ ├── config
│ │ ├── dev.yml
│ │ ├── index.ts
│ │ ├── prod.yml
│ │ └── test.yml
│ ├── main.ts
│ └── user
│ ├── dto
│ │ ├── create-user.dto.ts
│ │ └── update-user.dto.ts
│ ├── entities
│ │ └── user.entity.ts
│ ├── user.controller.ts
│ ├── user.module.ts
│ └── user.service.ts
├── tsconfig.build.json
└── tsconfig.json
config/index.ts
是动态加载所有配置的入口页
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';
const configFileNameObj = {
development: 'dev',
test: 'test',
production: 'prod',
};
const env = process.env.NODE_ENV;
export default () => {
return yaml.load(
readFileSync(join(__dirname, `./${configFileNameObj[env]}.yml`), 'utf8'),
) as Record<string, any>;
};
这里有个需要注意的地方导入js-yaml
的时候一定要用* as yaml
或者require()
,不然的话会报错load
方法不存在,当然你也可以单独导入方法,看你喜好。
还需要注意的是导出必须是个函数,以为配置包的load
方法只接受函数数组
yml
格式的配置文件比如dev.yml
# 数据库配置
db:
mysql:
host: 'localhost'
username: 'root'
password: '123456'
database: 'kapok'
port: 3306
charser: 'utf8mb4'
logger: 'advanced-console'
logging: true
multipleStatements: true
dropSchema: false
synchronize: true
supportBigNumbers: true
bigNumberStrings: true
然后在app.module.ts
中配置导入yml
配置
import configuration from './config/index';
@Module({
imports: [
ConfigModule.forRoot({
cache: true,
load: [configuration],
isGlobal: true,
}),
],
controllers: [],
providers: [],
})
现在如果启动项目的话,大概率yml
文件会找不到而报错,此时还需要去配置下cli
的配置文件nest-cli.json
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": ["**/*.yml"],
"deleteOutDir": true,
"watchAssets": true
},
"generateOptions": {
"spec": false
}
}
特别注意的就是这个打包配置compilerOptions
中的assets
,这个要配置成所有的yml
文件,这样才能在dist
中找到配置的配置文件
dist
│ ├── app.module.d.ts
│ ├── app.module.js
│ ├── app.module.js.map
│ ├── config
│ │ ├── dev.yml
│ │ ├── index.d.ts
│ │ ├── index.js
│ │ ├── index.js.map
│ │ ├── prod.yml
│ │ └── test.yml
接下来还需要去修改一下npm
的script
命令,加上环境变量来运行对应的环境配置,先安装一个cross-env
npm i cross-env -D
然后修改对应环境的脚本命令
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
"start:test": "cross-env NODE_ENV=test node dist/main",
"start:prod": "cross-env NODE_ENV=production node dist/main",
启动项目的时候就能加载对应的环境配置了。
使用配置
使用配置的话,需要导入@nestjs/config
提供的服务来获取配置信息,比如在user
模块使用配置,因为配置已经设置为全局可用,所以不再需要在user
模块中导入了,可以直接使用
import { ConfigService } from '@nestjs/config';
@Controller('user')
export class UserController {
constructor(
private readonly userService: UserService,
private configService: ConfigService,
) {}
@Get()
findAll() {
console.log('configService===', this.configService.get('db'));
return this.userService.findAll();
}
这里能打印出对应的配置说明多配置环境可以使用了,这是在控制器里面使用这个配置服务,通过注入的方式,那如果是想在模块文件中使用配置服务怎么办,就拿数据库的配置来举例
@Module({
imports: [
// 数据库
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return {
type: 'mysql',
// 可能不再支持这种方式,entities 将改成接收 实体类的引用
//
// entities: [`${__dirname}/**/*.entity{.ts,.js}`],
autoLoadEntities: true,
keepConnectionAlive: true,
...config.get('db.mysql'),
// cache: {
// type: 'ioredis',
// ...config.get('redis'),
// alwaysEnabled: true,
// duration: 3 * 1000, // 缓存3s
// },
} as TypeOrmModuleOptions;
},
}),
],
controllers: [],
providers: [],
})
由配置的例子可以看出通过一个useFactory
函数,然后将配置服务作为参数传递进行使用,就可以在模块文件中使用配置信息了
Joi
虽然对配置做了配置文件的抽离,但是配置写的对不对并不能过判断出来,这就需要一个校验的工具,这个工具是Joi,通过这个包可以对配置信息作校验,如果有问题会抛出错误,及时检验。
首先安装joi
包
npm i joi -S
假设去校验开发环境的变量配置和服务启动的端口,可以在配置服务的地方这样做
ConfigModule.forRoot({
cache: true,
load: [configuration],
isGlobal: true,
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test')
.default('development'),
PORT: Joi.number().default(3000),
}),
validationOptions: {
// 控制是否允许环境变量中未知的键 默认为true
allowUnknown: true,
// 在遇到第一个错误时就停止验证,如果为false就返回所有错误,默认为false
abortEarly: true,
},
}),
这个简单的校验就完成了,通过配置模块提供的配置,很轻松的就接入了配置的校验,这里有个需要注意的地方,引入joi
的时候如果是使用esm的方式就需要全部重命名导入
import * as Joi from 'joi'
不然是会报错方法找不到的,和js-yaml
一样的问题,因为没有默认导出全部
小结
到这里基本是搭建好了一个多环境开发的基础项目,还有一些关于开发的接口方面的配置比如jwt
、统一封装返回体
、统一拦截报错
等,下次在整理吧,希望对你有帮助!!!
原文链接:https://juejin.cn/post/7216536069285707813 作者:water