config
配置书写的种类:
- .env 文件
- 使用工厂函数生成配置对象
- 使用服务配置文件
依赖
yarn add @nestjs/config --dev
定义模块
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
@Module({
imports: [ConfigModule.forRoot()]
})
export class AppModule {}
// 手动添加配置
// 单配置
ConfigModule.forRoot({
envFilePath: ".development.env"
});
// 多配置
ConfigModule.forRoot({
envFilePath: [".env.development.local", ".env.development"]
});
// 关闭
ConfigModule.forRoot({
ignoreEnvFile: true
});
.env 配置方式
DATABASE_USER=test
DATABASE_PASSWORD=test
使用工厂函数代替 .env 文件
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 {}
ConfigModule
@Module({
imports: [ConfigHostModule],
providers: [
{
provide: ConfigService,
useExisting: CONFIGURATION_SERVICE_TOKEN
}
],
exports: [ConfigHostModule, ConfigService]
})
export class ConfigModule {
static forRoot(options: ConfigModuleOptions = {}): DynamicModule {
/* impl*/
}
static forFeature(config: ConfigFactory) {
/* impl*/
}
private static loadEnvFile(
options: ConfigModuleOptions
): Record<string, any> {
/* impl*/
}
private static assignVariablesToProcess(config: Record<string, any>) {
/* impl*/
}
private static mergePartial(
host: Record<string, any>,
item: Record<string, any>,
provider: FactoryProvider
) {
/* impl*/
}
private static getSchemaValidationOptions(options: ConfigModuleOptions) {
/* impl*/
}
}
使用服务
我们是 Module 的方式,所以可以在模块中 imports 一个 configService,然后依赖注入,使用模块服务。
// 导入符模块
@Module({
imports: [ConfigModule],
// ...
})
// 使用
constructor(private configService: ConfigService) {}
// 获取配置
// get an environment variable
const dbUser = this.configService.get<string>('DATABASE_USER');
// get a custom configuration value
const dbHost = this.configService.get<string>('database.host');
服务 get 源码
import { Inject, Injectable, Optional } from "@nestjs/common";
import get from "lodash.get";
import { isUndefined } from "util";
import {
CONFIGURATION_TOKEN,
VALIDATED_ENV_PROPNAME
} from "./config.constants";
import { NoInferType } from "./types";
@Injectable()
export class ConfigService<K = Record<string, any>> {
constructor(
@Optional()
@Inject(CONFIGURATION_TOKEN)
private readonly internalConfig: Record<string, any> = {}
) {}
get<T = any>(propertyPath: keyof K, defaultValue?: T): T | undefined {
const validatedEnvValue = get(
this.internalConfig[VALIDATED_ENV_PROPNAME],
propertyPath
);
if (!isUndefined(validatedEnvValue)) {
return (validatedEnvValue as unknown) as T;
}
const processValue = get(process.env, propertyPath);
if (!isUndefined(processValue)) {
return (processValue as unknown) as T;
}
const internalValue = get(this.internalConfig, propertyPath);
return isUndefined(internalValue) ? defaultValue : internalValue;
}
}
使用命令空间
@nestjs/config 包中给我们提供了一个 registerAs() 方法,可以帮我们提供命名空间能力。
import { registerAs } from "@nestjs/config";
// 定义配置
export default registerAs("database", () => ({
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT || 5432
}));
// 模块导入
import databaseConfig from "./config/database.config";
@Module({
imports: [
ConfigModule.forRoot({
load: [databaseConfig]
})
]
})
export class AppModule {}
// 注入服务,从服务中获取
const dbHost = this.configService.get < string > "database.host";
源码
export function registerAs<T extends ConfigFactory = ConfigFactory>(
token: string,
configFactory: T
): T & ConfigFactoryKeyHost {
Object.defineProperty(configFactory, PARTIAL_CONFIGURATION_KEY, {
configurable: false,
enumerable: false,
value: token,
writable: false
});
Object.defineProperty(configFactory, PARTIAL_CONFIGURATION_PROPNAME, {
configurable: false,
enumerable: false,
value: getConfigToken(token),
writable: false
});
return configFactory as T & ConfigFactoryKeyHost;
}
参考
- 12 因素 app: 12factor.net/