nest 中使用 dotenv、joi 实现各环境中数据库密码账号等配置以及校验
链接 mysql,Redis 之前,我们需要准备好如何配置各环境中不同的变量,这非常重要
首先,要确保密码,密钥等重要信息不可保留在代码中
其次要为部署测试环境,生产环境使用的有可能是不一样的配置的准备
如果不想自己写这么多,可以看文章后半段,更推荐使用nest-redis库
安装依赖包
yarn add dotenv joi
使用 dotenv 可以让我们免于在各个文件中引入配置文件将环境相关的配置独立于代码之外,也可以很好的解决敏感信息的泄漏
joi 模块,主要用来实现验证数据是否符合指定的规则。
创建.env 文件
config/env 目录下新建 development.env 文件, 看你喜欢
看名字就知道是做什么的,这里就不多介绍了,gitignore 中可以把这个文件去掉,多人开发时本地配置文件根据自己的环境配置
安装 cross-env 修改 package.json scripts, 其他构建成测试或者生产的,配合 xxx.env 文件自行定义,自然的,除了本地配置
当打包到生产环境时,production.env 不会被打包,这将在后续我们部署到 Linux 中讲到
"start:dev": "cross-env NODE_ENV=development nest start --watch",
yarn add cross-env --dev
SERVER_PORT = 3000
BS_URL = hkapi
# ORM_LOADING_PATH=src
DB_TYPE = mysql
DB_HOST = localhost
DB_PORT = 3306
DB_USER = root
DB_PWD = root
DB_DB = hk_admin
DB_SYN = true
# DB_DRO = true # 每次启动是否删除数据库表重新创建
# Redis 配置
RD_PORT= 6379
RD_HOST= '127.0.0.1'
RD_DB= 0
RD_PWD= '123456'
根据环境读取不同的配置文件.env
config/config.module.ts
import { Module } from "@nestjs/common";
import { ConfigService } from "./config.service";
@Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(`${process.env.NODE_ENV}.env`), // 读取当前环境的env文件
},
],
exports: [ConfigService],
})
export class ConfigModule {}
可以选择对所有配置的校验规则,并且提供换回方法
config/config.service.ts
import { Injectable } from "@nestjs/common";
import * as dotenv from "dotenv";
import * as Joi from "joi";
import * as fs from "fs";
export interface EnvConfig {
[key: string]: string;
}
@Injectable()
export class ConfigService {
private readonly envConfig: EnvConfig;
constructor(filePath: string) {
// 读取文件变量挂到envConfig
const config = dotenv.parse(fs.readFileSync(filePath));
this.envConfig = this.validateInput(config);
}
private validateInput(envConfig: EnvConfig): EnvConfig {
const envVarsSchema: Joi.ObjectSchema = Joi.object({
SERVER_PORT: Joi.number().default(3000),
BS_URL: Joi.string(),
DB_HOST: Joi.string(),
DB_TYPE: Joi.string(),
DB_PORT: Joi.number(),
DB_USER: Joi.string(),
DB_PWD: Joi.string(),
DB_DB: Joi.string(),
DB_SYN: Joi.boolean(),
RD_PORT: Joi.number().default(6379),
RD_HOST: Joi.string().default("127.0.0.1"),
RD_DB: Joi.number().default(0),
RD_PWD: Joi.string(),
});
const { error, value: validatedEnvConfig } = envVarsSchema.validate(
envConfig
);
if (error) {
throw new Error(`Config validation error: ${error.message}`);
}
return validatedEnvConfig;
}
get port(): number {
return Number(this.envConfig.SERVER_PORT);
}
get bsUrl(): string {
return this.envConfig.BS_URL;
}
// mysql相关
get dbType(): string {
return this.envConfig.DB_TYPE;
}
get dbHost(): string {
return this.envConfig.DB_HOST;
}
get dbPort(): number {
return Number(this.envConfig.DB_PORT);
}
get dbUser(): string {
return this.envConfig.DB_USER;
}
get dbPwd(): string {
return this.envConfig.DB_PWD;
}
get dbDb(): string {
return this.envConfig.DB_DB;
}
get dbSyn(): boolean {
return Boolean(this.envConfig.DB_SYN);
}
// Redis相关
get rdPort(): number {
return Number(this.envConfig.RD_PORT);
}
get rdHost(): string {
return String(this.envConfig.RD_HOST);
}
get rdDb(): number {
return Number(this.envConfig.RD_DB);
}
get rdPwd(): string {
return String(this.envConfig.RD_PWD);
}
}
检验
此时我们修改一下 以及 main.ts 的代码,看一下是否成功
main.ts
import {
NestFactory
} from "@nestjs/core";
import {
AppModule
} from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
从这获取config
const configService = app.get(ConfigService);
app.setGlobalPrefix(configService.get("bsUrl")); // 全局基础URL,届时所有路由前都会有这个基础的url
await app.listen(configService.get("port"), () => {
console.log(`http://localhost:${configService.get("port")}/${configService.get("bsUrl")}`);
});
}
bootstrap();
app.service.ts
import {
Injectable
} from '@nestjs/common';
import {
ConfigService
} from './config/config.service';
@Injectable()
export class AppService {
constructor(public readonly config: ConfigService) {}
getHello(): string {
return 'Hello World!' + this.config.port;
}
}
重新运行 yarn run start:dev
可以看到输出 http://localhost:3000/api
浏览器访问 http://localhost:3000/api
,可以看到 Hello World!3000
使用 @nestjs/config 库,不管哪种方式,其实都差不多
以上还是需要自己来实现略显麻烦,@nestjs/config 库为我们做了许多,我们只需要简单的引入使用即可
common文件夹新建config/configuration.ts
@nestjs/config读取后会根据我们的函数输出json,这里看项目需要是否需要分类等
nest8以上,有可能有这个报错Please make sure that the argument ModuleRef at index [1] is available in the RedisCo
export default () => ({
http: {
port: process.env.SERVER_PORT || 3000,
bsUrl: process.env.BS_URL || "",
},
dataBase: {
dbType: process.env.DB_TYPE || "mysql",
dbHost: process.env.DB_HOST || "localhost",
dbPort: parseInt(process.env.DB_PORT, 10) || 3306,
dbUser: process.env.DB_USER || "root",
dbPwd: process.env.DB_PWD || "root",
dbDb: process.env.DB_DB,
dbSyn: process.env.DB_SYN || true,
},
redis: {
namespace: process.env.RD_NAME_SPACE || "default",
port: parseInt(process.env.RD_PORT, 10) || 3306,
db: parseInt(process.env.RD_DB, 10) || 0,
host: process.env.RD_HOST || "127.0.0.1",
password: process.env.RD_PWD || "",
},
jwt: {
secret: process.env.SECRET || "123456",
expiresIn: process.env.EXPIRES_IN || "168h",
},
});
使用方法如下注释
app.module.ts
import {
ConfigModule,
ConfigService
} from "@nestjs/config";
@Module({
imports: [
// 此处配置configmodule
ConfigModule.forRoot({
isGlobal: true,
cache: true,
envFilePath: `${process.env.NODE_ENV}.env`,
load: [configuration],
}),
// 在这提供给TypeOrmModule
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
const dataBase = configService.get("dataBase");
return {
type: dataBase.dbType,
host: dataBase.dbHost,
port: dataBase.dbPort,
username: dataBase.dbUser,
password: dataBase.dbPwd,
database: dataBase.dbDb,
entities: [__dirname + "/entities/**/*.entity{.ts,.js}"],
synchronize: true,
}
as TypeOrmModuleAsyncOptions;
},
}),
// 提供给Redismodule
RedisModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
return {
closeClient: true,
readyLog: true,
config: configService.get("redis"),
};
},
}),
],
})
export class AppModule {}
在main.ts中使用也是同理
import {
ConfigService
} from "@nestjs/config";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
const http = configService.get("http");
// 全局基础URL
app.setGlobalPrefix(http.bsUrl);
await app.listen(http.port, () => {
console.log(`http://localhost:${http.port}/${http.bsUrl}`);
});
}
bootstrap();
下一遍,将利用配置,链接数据库,以 mysql 为例