FORMA

配置管理(Config 模块)

@nestjs/config 加载 .env 并提供 ConfigService。见 工程化 env

@nestjs/config 模块是 NestJS 官方提供的配置管理工具,它基于 dotenv,可以方便地加载环境变量、支持多环境配置、提供类型安全的配置服务,并可对环境变量进行验证。

一、@nestjs/config 模块

安装依赖:

bash
npm install @nestjs/config

该模块导出了 ConfigModuleConfigServiceConfigModule 需要导入到根模块(通常是 AppModule),以便在整个应用中使用 ConfigService

二、加载环境变量:ConfigModule.forRoot()

最简单的用法是调用 ConfigModule.forRoot(),它会自动读取项目根目录下的 .env 文件,并将变量合并到 process.env 中。

ts
// app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";

@Module({
  imports: [
    ConfigModule.forRoot(), // 加载 .env 文件
  ],
})
export class AppModule {}

默认情况下,它会查找 .env 文件。如果文件不存在,则只读取系统环境变量。可以通过 envFilePath 自定义路径:

ts
ConfigModule.forRoot({
  envFilePath: ".env.development",
});

也可以指定多个文件(后面的会覆盖前面的):

ts
ConfigModule.forRoot({
  envFilePath: [".env.development.local", ".env.development"],
});

三、使用配置服务:ConfigService

ConfigService 提供了 get() 方法来获取配置变量。

ts
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";

@Injectable()
export class AppService {
  constructor(private configService: ConfigService) {}

  getDatabaseHost(): string {
    return this.configService.get<string>("DB_HOST");
  }
}

get() 方法支持默认值:

ts
const port = this.configService.get<number>("PORT", 3000);

ConfigService 还提供了 getOrThrow() 方法,如果变量不存在会抛出异常(适用于必需变量)。

ts
const apiKey = this.configService.getOrThrow("API_KEY");

四、类型安全

@nestjs/config 本身不会自动生成类型,但可以使用 TypeScript 接口结合泛型来增强类型安全。

方案一:定义配置接口

ts
// config.interface.ts
export interface EnvConfig {
  DB_HOST: string;
  DB_PORT: number;
  NODE_ENV: "development" | "production" | "test";
  API_KEY: string;
}

然后在服务中使用类型断言:

ts
const dbHost = this.configService.get<string>('DB_HOST');
// 或封装成类型化的方法
get<T = any>(key: keyof EnvConfig): T {
  return this.configService.get<T>(key);
}

方案二:使用 ConfigModule.forRootvalidationSchema 配合 Joi 生成类型

Joi 校验时可以自动推导类型,但仍需手动定义接口。

更好的做法:使用 @nestjs/configConfigModule.forRootload 选项加载自定义配置函数,该函数返回带有类型的对象,然后 ConfigService.get 会继承该类型(但仅适用于自定义配置节)。对于环境变量,通常还是需要手动断言。

五、验证环境变量

为了防止因缺少必要的环境变量导致应用异常,可以对环境变量进行验证。@nestjs/config 支持两种验证方式:

1. Joi 验证

安装 Joi:

bash
npm install joi

ConfigModule.forRoot() 中传入 validationSchema

ts
import * as Joi from "joi";

@Module({
  imports: [
    ConfigModule.forRoot({
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().valid("development", "production", "test").default("development"),
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.number().default(5432),
        API_KEY: Joi.string().required(),
      }),
    }),
  ],
})
export class AppModule {}

如果环境变量不符合 schema,应用启动时会抛出错误。

2. class-validator 验证(配合自定义配置类)

虽然 @nestjs/config 不直接支持 class-validator,但可以通过自定义配置类 + 工厂函数实现:

ts
// config.validation.ts
import { plainToClass } from "class-transformer";
import { validateSync } from "class-validator";

export class EnvVariables {
  @IsString()
  DB_HOST: string;

  @IsNumber()
  DB_PORT: number;

  @IsString()
  @IsIn(["development", "production", "test"])
  NODE_ENV: string;
}

export function validate(config: Record<string, unknown>) {
  const validatedConfig = plainToClass(EnvVariables, config, {
    enableImplicitConversion: true,
  });
  const errors = validateSync(validatedConfig, { skipMissingProperties: false });
  if (errors.length) {
    throw new Error(`Config validation error: ${errors.toString()}`);
  }
  return validatedConfig;
}

然后在 ConfigModule.forRoot 中使用 validate 函数:

ts
ConfigModule.forRoot({
  validate,
});

六、多环境配置

通常开发、测试、生产环境需要使用不同的配置。可以通过 envFilePath 结合环境变量 NODE_ENV 来动态加载。

ts
// app.module.ts
const envFilePath = `.env.${process.env.NODE_ENV || "development"}`;

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath,
      isGlobal: true, // 使 ConfigModule 全局可用,避免在每个模块重复导入
    }),
  ],
})
export class AppModule {}

也可以使用多个文件,让特定环境的配置覆盖默认配置:

ts
ConfigModule.forRoot({
  envFilePath: [".env", ".env.development"], // 后面的覆盖前面的
});

常见文件命名

  • .env:通用配置(可提交)
  • .env.local:本地覆盖(不提交)
  • .env.development:开发环境
  • .env.production:生产环境

七、自定义配置文件(load 属性)

除了环境变量,有时需要从 JSON 或 YAML 文件中加载配置。可以使用 load 属性,它接受一个返回配置对象的函数数组。

ts
import { readFileSync } from "fs";
import * as yaml from "js-yaml";

const loadYamlConfig = () => {
  const configFile = process.env.CONFIG_FILE || "./config.yaml";
  return yaml.load(readFileSync(configFile, "utf8")) as Record<string, any>;
};

@Module({
  imports: [
    ConfigModule.forRoot({
      load: [loadYamlConfig],
    }),
  ],
})
export class AppModule {}

load 中的函数可以返回对象,该对象会合并到最终的配置中,通过 ConfigService.get('key') 访问。注意,若环境变量和自定义配置有相同 key,环境变量优先级更高(取决于 ConfigModule 的配置顺序,通常 load 的配置会先加载,然后被环境变量覆盖)。

实际应用

  • 敏感信息(数据库密码、API 密钥)放在环境变量中。
  • 静态配置(如功能开关、业务规则)放在 YAML/JSON 配置文件中。

八、全局使用 ConfigModule

为了避免在每个模块中重复导入 ConfigModule,可以将其设置为全局模块:

ts
ConfigModule.forRoot({
  isGlobal: true,
});

这样其他模块中无需再导入 ConfigModule 即可注入 ConfigService

九、最佳实践

  1. 定义配置接口:为环境变量和自定义配置维护 TypeScript 接口,并在读取时使用类型断言。
  2. 验证环境变量:在应用启动时使用 Joi 或 class-validator 进行验证,确保必需变量存在且格式正确。
  3. 分离敏感信息:不要将 .env 文件提交到版本控制,使用 .env.example 给出模板。
  4. 使用默认值:为可选配置提供合理的默认值,避免因缺失变量导致服务启动失败。
  5. 生产环境禁用 synchronize 等危险选项:通过环境变量控制。

十、示例:完整的配置模块

bash
# .env.example
DB_HOST=localhost
DB_PORT=5432
NODE_ENV=development
ts
// app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import * as Joi from "joi";

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV}`,
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().valid("development", "production", "test").required(),
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.number().default(5432),
      }),
    }),
  ],
})
export class AppModule {
  constructor(private configService: ConfigService) {
    // 使用配置
    const dbHost = this.configService.get<string>("DB_HOST");
    console.log(`Database host: ${dbHost}`);
  }
}

总结

功能方法
加载环境变量ConfigModule.forRoot()
读取配置ConfigService.get() / getOrThrow()
类型安全自定义接口 + 类型断言
验证Joi schema 或 class-validator
多环境通过 envFilePath 动态指定文件
自定义配置load 属性加载 YAML/JSON 文件
全局可用isGlobal: true

使用 @nestjs/config 可以系统地管理配置,使应用在不同环境下保持行为一致且安全。

参考文献

以下链接在编写时均可正常访问:

资料说明
NestJS 文档官方
Configuration本章主题
Request lifecycle执行顺序

Series

new

4 / 19