组态
组态
用于在不同环境中
运行的应用程序。根据环境,应使用各种配置变量集。例如,本地环境很可能在特定数据库凭证上进行中继,仅对本地数据库实例有效。为了解决这个问题,我们过去常常利用.env
保存键值对的文件,其中每个键代表一个特定的值,因为这种方法非常方便。
但是当我们使用process
全局对象时,由于测试类可能直接使用它,因此很难保持测试清洁。另一种方法是创建一个抽象层,ConfigModule
它暴露出一个ConfigService
带有加载的配置变量。
安装
某些平台会自动将我们的环境变量附加到process.env
全局。但是,在当地环境中,我们必须手动处理它。为了解析我们的环境文件,我们将使用dotenv包。
$ npm i --save dotenv
服务
首先,让我们创建一个ConfigService
类。
JS
import * as dotenv from 'dotenv';
import * as fs from 'fs';
export class ConfigService {
private readonly envConfig: { [key: string]: string };
constructor(filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath))
}
get(key: string): string {
return this.envConfig[key];
}
}
该类采用单个参数a filePath
,它是.env
文件的路径。get()
提供该方法以允许访问envConfig
保存环境文件内定义的每个属性的私有对象。
最后一步是创建一个ConfigModule
。
JS
import { Module } from '@nestjs/common';
import { ConfigService } from './config.service';
@Module{
providers: [
{
provide: ConfigService,
useValue: new ConfigService(`${process.env.NODE_ENV}.env`),
},
],
exports: [ConfigService],
})
export class ConfigModule {}
该ConfigModule
寄存器中ConfigService
,出口它。另外,我们传递了一个.env
文件路径。根据实际执行环境,此路径将有所不同。现在,您可以简单地ConfigService
在任何地方注入,并根据传递的密钥提取特定值。示例.env
文件如下所示:
development.env
DATABASE_USER=test
DATABASE_PASSWORD=test
使用ConfigService
要从我们访问环境变量
,ConfigService
我们需要注入它。因此我们首先需要导入模块。
app.module.ts
@Module{
imports: [ConfigModule],
...
})
之后,您可以使用注入令牌注入它。默认情况下,令牌等于类名(在我们的示例中ConfigService
)。
app.service.ts
@Injectable()
export class AppService {
private isAuthEnabled: boolean;
constructor(config: ConfigService) {
// Please take note that this check is case sensitive!
this.isAuthEnabled = config.get('IS_AUTH_ENABLED') === 'true' ? true : false;
}
}
ConfigModule
您也可以将其声明ConfigModule
为全局模块,而不是在所有模块中重复导入。
高级配置
我们刚刚实施了一个基础ConfigService
。但是,这种方法有一些缺点,我们现在将解决:
- 缺少环境变量的名称和类型(没有IntelliSense)
验证
我们将从提供的环境变量的验证开始。如果未提供所需的环境变量或者它们不符合您的预定义要求,则可能会抛出错误。为此,我们将使用npm包Joi。使用Joi,您可以定义一个对象模式并根据它来验证JavaScript对象。
安装Joi及其类型(适用于TypeScript
用户):
$ npm install --save joi
$ npm install --save-dev @types/joi
一旦安装了包,我们就可以转移到我们的ConfigService
。
config.service.ts
import * as Joi from 'joi';
import * as fs from 'fs';
export interface EnvConfig {
[key: string]: string;
}
export class ConfigService {
private readonly envConfig: EnvConfig;
constructor(filePath: string) {
const config = dotenv.parse(fs.readFileSync(filePath)
this.envConfig = this.validateInput(config
}
/**
* Ensures all needed variables are set, and returns the validated JavaScript object
* including the applied default values.
*/
private validateInput(envConfig: EnvConfig): EnvConfig {
const envVarsSchema: Joi.ObjectSchema = Joi.object{
NODE_ENV: Joi.string()
.valid(['development', 'production', 'test', 'provision'])
.default('development'),
PORT: Joi.number().default(3000),
API_AUTH_ENABLED: Joi.boolean().required(),
}
const { error, value: validatedEnvConfig } = Joi.validate(
envConfig,
envVarsSchema,
if (error) {
throw new Error(`Config validation error: ${error.message}`
}
return validatedEnvConfig;
}
}
由于我们的设置默认值NODE_ENV
和PORT
如果我们不提供环境文件这些变量验证也不会失败。不过,我们需要明确提供API_AUTH_ENABLED
。如果我们的.env文件中的变量不是模式的一部分,验证也会抛出错误。此外,Joi尝试将env字符串转换为正确的类型。
类属性
对于每个配置属性,我们必须添加一个getter函数。
config.service.ts
get isApiAuthEnabled(): boolean {
return Boolean(this.envConfig.API_AUTH_ENABLED
}
用法示例
现在我们可以直接访问类属性。
app.service.ts
@Injectable()
export class AppService {
constructor(config: ConfigService) {
if (config.isApiAuthEnabled) {
// Authorization is enabled
}
}
}