nestjs ☸️ 🎡⚙️ 配置

1,544 阅读2分钟

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;
}

参考

  1. 12 因素 app: 12factor.net/