NestJS 配置管理完整方案

以下是 NestJS 配置管理完整方案 + 代码汇总,覆盖四大配置方案、两种依赖注入模式、三种验证体系,所有代码均可直接落地使用。

一、四大配置方案(含完整实现代码) 方案 1:原生 dotenv 方案(底层基础)

适用场景:纯 Node.js 简易工具、极简原型项目,不依赖 Nest 框架。

核心原理:零依赖库,直接将 .env 文件键值对注入 process.env。

arduino 复制代码
1. 安装依赖
bash
运行
npm i dotenv
2. 根目录创建 .env
env
TOKEN_SECRET=your-long-random-secret
DB_USER=admin
DB_PASSWORD=example123
3. 入口文件使用
javascript
运行
// index.js
require('dotenv').config();

console.log(process.env.DB_USER); // admin
console.log(process.env.TOKEN_SECRET); // your-long-random-secret

方案 2:官方 @nestjs/config + 多环境 .env(主流首选)

适用场景:绝大多数中小型 NestJS 项目、容器化 / 云原生部署,是官方推荐的标准方案。

核心特性:多环境文件分层合并、全局注入、配合枚举消除魔法字符串。

css 复制代码
1. 安装依赖
bash
运行
npm i @nestjs/config dotenv
npm i -D cross-env
ini 复制代码
2. 创建多环境配置文件
根目录下创建三层配置文件,遵循「公共兜底 + 环境覆盖」原则:
env
# .env 公共兜底配置
APP_NAME=nest-admin
DB_URL=https://default.example.com
env
# .env.development 开发环境
DB=mysql-dev
DB_HOST=127.0.0.1
DB_PORT=3306
env
# .env.production 生产环境
DB=mysql-prod
DB_HOST=10.0.0.1
DB_PORT=3306
ini 复制代码
3. 枚举统一管理配置键(避免魔法字符串)
typescript
运行
// src/enum/config.const.ts
export enum ConfigEnum {
  DB = 'DB',
  DB_HOST = 'DB_HOST',
  DB_PORT = 'DB_PORT',
  DB_URL = 'DB_URL',
  PORT = 'PORT',
}
typescript 复制代码
4. 全局注册配置模块
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as dotenv from 'dotenv';
import { UserModule } from './user/user.module';

// 动态生成环境配置文件路径
const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 全局生效,所有模块无需重复导入
      envFilePath,    // 加载当前环境的差异化配置
      // 加载公共兜底配置,环境配置会覆盖同名变量
      load: [() => dotenv.config({ path: '.env' })],
    }),
    UserModule,
  ],
})
export class AppModule {}
go 复制代码
5. 配置启动脚本(package.json)
json
{
  "scripts": {
    "start:dev": "cross-env NODE_ENV=development nest start --watch",
    "start:prod": "cross-env NODE_ENV=production node dist/main"
  }
}
kotlin 复制代码
6. 业务代码中使用
typescript
运行
// src/user/user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.const';

@Controller('user')
export class UserController {
  constructor(private configService: ConfigService) {}

  @Get()
  getConfig() {
    const dbType = this.configService.get(ConfigEnum.DB);
    const dbHost = this.configService.get(ConfigEnum.DB_HOST);
    const dbUrl = this.configService.get(ConfigEnum.DB_URL);
    return { dbType, dbHost, dbUrl };
  }
}

方案 3:YAML 分层配置方案(复杂配置场景)

适用场景:配置层级深、数量多的中大型项目,YAML 格式对嵌套结构可读性更强。

核心原理:js-yaml 解析文件 + lodash.merge 深度合并公共配置与环境配置。

css 复制代码
1. 安装依赖
bash
运行
npm i js-yaml lodash
npm i -D @types/js-yaml @types/lodash
yaml 复制代码
2. 创建 YAML 配置文件
在根目录 config 文件夹下创建三个文件:
yaml
# config/config.yml 公共基础配置
db:
  mysql1:
    host: 127.0.0.1
    port: 3306
    name: default-db
  mysql2:
    host: 127.0.0.1
    port: 3306
    name: default-db2
yaml
# config/config.development.yml 开发环境覆盖
db:
  mysql1:
    name: mysql-dev
  mysql2:
    name: mysql-dev2
yaml
# config/config.production.yml 生产环境覆盖
db:
  mysql1:
    name: mysql-prod
  mysql2:
    name: mysql-prod2
javascript 复制代码
3. 编写配置加载函数
typescript
运行
// src/config/configuration.ts
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';
import { merge } from 'lodash';

// 公共配置文件路径
const commonFilePath = join(__dirname, '../../config/config.yml');
// 环境配置文件路径
const envFilePath = join(
  __dirname,
  '../../config',
  `config.${process.env.NODE_ENV || 'development'}.yml`
);

// 解析并合并配置
const commonConfig = yaml.load(readFileSync(commonFilePath, 'utf8'));
const envConfig = yaml.load(readFileSync(envFilePath, 'utf8'));

// 导出工厂函数,环境配置会深度覆盖公共配置
export default () => {
  return merge(commonConfig, envConfig);
};
typescript 复制代码
4. 注册到配置模块
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configuration], // 加载自定义 YAML 配置
    }),
  ],
})
export class AppModule {}
kotlin 复制代码
5. 读取嵌套配置
typescript
运行
// 支持点语法读取嵌套属性
const mysql1Config = this.configService.get('db.mysql1');
// { host: '127.0.0.1', port: 3306, name: 'mysql-dev' }

方案 4:node-config 方案(独立配置体系)

适用场景:偏好约定式配置、不依赖 Nest 注入体系的项目,原生支持多环境自动合并。

核心特性:按文件名约定自动加载对应环境配置,支持 JSON/YAML 双格式。

css 复制代码
1. 安装依赖
bash
运行
npm i config cross-env
npm i -D @types/config
arduino 复制代码
2. 创建配置文件
在根目录 config 文件夹下创建:
json
// config/default.json 公共默认配置
{
  "server": {
    "appName": "nest-admin"
  },
  "db": {
    "host": "localhost",
    "port": 3306
  }
}
json
// config/development.json 开发环境
{
  "db": {
    "username": "dev_user",
    "password": "dev123"
  }
}
json
// config/production.json 生产环境
{
  "db": {
    "username": "prod_user",
    "password": "prod@2024"
  }
}
csharp 复制代码
3. 直接读取使用
typescript
运行
import * as config from 'config';

// 读取嵌套配置
const dbConfig = config.get('db');
const dbHost = config.get<string>('db.host');
json 复制代码
4. 启动脚本
json
{
  "scripts": {
    "dev": "cross-env NODE_ENV=development node index.js",
    "prod": "cross-env NODE_ENV=production node index.js"
  }
}
补充:该库也支持 YAML 格式,只需将文件后缀改为 .yml 即可,使用方式完全一致。

二、进阶:依赖注入式配置管理(含 Inject 实现)

模式 1:registerAs 命名空间 + @Inject 注入

适用场景:中大型项目,按业务域拆分配置,按需精准注入,符合依赖注入设计原则。

arduino 复制代码
1. 按模块定义命名空间配置
typescript
运行
// src/config/database.config.ts
import { registerAs } from '@nestjs/config';

// 第一个参数:命名空间名称;第二个参数:配置工厂函数
export const databaseConfig = registerAs('database', () => ({
  host: process.env.DATABASE_HOST || 'localhost',
  port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
  username: process.env.DATABASE_USER || 'postgres',
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_NAME || 'app_db',
}));
typescript
运行
// src/config/app.config.ts
import { registerAs } from '@nestjs/config';

export const appConfig = registerAs('app', () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  env: process.env.NODE_ENV || 'development',
  apiPrefix: process.env.API_PREFIX || '/api',
}));
typescript 复制代码
2. 全局注册命名空间
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { databaseConfig } from './config/database.config';
import { appConfig } from './config/app.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: ['.env', `.env.${process.env.NODE_ENV || 'development'}`],
      load: [databaseConfig, appConfig], // 注册所有命名空间
    }),
  ],
})
export class AppModule {}
typescript 复制代码
3. 服务中通过 @Inject 精准注入
typescript
运行
// src/database/database.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { databaseConfig } from '../config/database.config';
import type { ConfigType } from '@nestjs/config';

@Injectable()
export class DatabaseService {
  constructor(
    // 注入 database 命名空间的完整配置对象
    @Inject(databaseConfig.KEY)
    private dbConfig: ConfigType<typeof databaseConfig>,
  ) {}

  getConnection() {
    // 完整类型提示,无需手动声明接口
    return {
      host: this.dbConfig.host,
      port: this.dbConfig.port,
      user: this.dbConfig.username,
    };
  }
}

模式 2:forRootAsync + inject 工厂异步注入

适用场景:远程配置中心拉取、配置解密、依赖其他服务的动态配置场景。

typescript 复制代码
同步工厂 inject 示例
typescript
运行
// src/app.module.ts
import { Module, Logger } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

// 配置工厂函数,参数为注入的依赖
const configurationFactory = (logger: Logger) => {
  logger.log('加载应用配置...', 'ConfigFactory');
  return {
    app: { port: parseInt(process.env.PORT, 10) || 3000 },
    database: { host: process.env.DB_HOST },
  };
};

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configurationFactory],
      inject: [Logger], // 声明依赖,顺序与工厂函数参数一一对应
    }),
  ],
  providers: [Logger],
})
export class AppModule {}
typescript 复制代码
异步工厂 useFactory 示例(远程配置场景)
typescript
运行
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { HttpModule, HttpService } from '@nestjs/axios';

@Module({
  imports: [
    HttpModule,
    ConfigModule.forRootAsync({
      isGlobal: true,
      inject: [HttpService], // 注入依赖服务
      useFactory: async (httpService: HttpService) => {
        // 异步拉取远程配置中心配置
        const { data: remoteConfig } = await httpService.axiosRef.get(
          'https://config-center.example.com/app-config'
        );
        // 合并本地环境变量与远程配置
        return {
          ...remoteConfig,
          local: { env: process.env.NODE_ENV },
        };
      },
    }),
  ],
})
export class AppModule {}

三、配置验证三大体系(含完整实现代码)

方案 1:Joi 验证(经典成熟方案)

适用场景:JavaScript 项目、存量项目,校验规则丰富度要求高。

css 复制代码
1. 安装依赖
bash
运行
npm i joi
kotlin 复制代码
2. 模块中配置校验规则
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';

const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath,
      // Joi 校验 Schema
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production', 'test')
          .default('development'),
        PORT: Joi.number().default(3000),
        DB_HOST: Joi.string().ip().required(),
        DB_PORT: Joi.number().port().default(3306),
        DB_URL: Joi.string().domain(),
      }),
      validationOptions: {
        allowUnknown: false, // 禁止未定义的环境变量
        abortEarly: true,    // 遇到第一个错误即终止
      },
    }),
  ],
})
export class AppModule {} //配置不合法时,应用启动阶段直接抛出错误并终止,避免线上运行时故障。

方案 2:class-validator 验证(装饰器风格)

适用场景:团队技术栈统一使用装饰器风格,与接口 DTO 校验保持一致体验。

arduino 复制代码
1. 安装依赖
bash
运行
npm i class-validator class-transformer
typescript 复制代码
2. 定义校验类与验证函数
typescript
运行
// src/config/env.validation.ts
import { plainToInstance } from 'class-transformer';
import { IsEnum, IsNumber, IsString, IsIP, validateSync } from 'class-validator';

enum Environment {
  Development = 'development',
  Production = 'production',
  Test = 'test',
}

class EnvironmentVariables {
  @IsEnum(Environment)
  NODE_ENV: Environment;

  @IsNumber()
  PORT: number;

  @IsIP()
  DB_HOST: string;

  @IsString()
  DB_USER: string;
}

export function validate(config: Record<string, unknown>) {
  const validatedConfig = plainToInstance(EnvironmentVariables, config, {
    enableImplicitConversion: true, // 自动类型转换
  });
  const errors = validateSync(validatedConfig, {
    skipMissingProperties: false,
  });

  if (errors.length > 0) {
    throw new Error(`配置验证失败: ${errors.toString()}`);
  }
  return validatedConfig;
}
javascript 复制代码
3. 模块中注册
typescript
运行
import { validate } from './config/env.validation';

ConfigModule.forRoot({
  isGlobal: true,
  validate, // 注册自定义验证函数
});

方案 3:Zod 验证(TypeScript 首选)

适用场景:全新 TypeScript 项目,一次定义同时获得运行时校验与静态类型,开发体验最佳。

css 复制代码
1. 安装依赖
bash
运行
npm i zod
css 复制代码
2. 定义 Schema 并自动推导类型
typescript
运行
// src/config/env.schema.ts
import { z } from 'zod';

export const envSchema = z.object({
  NODE_ENV: z
    .enum(['development', 'production', 'test'])
    .default('development'),
  PORT: z.coerce.number().default(3000), // 自动字符串转数字
  DATABASE_HOST: z.string().min(1),
  DATABASE_PORT: z.coerce.number().int().positive(),
  DATABASE_USER: z.string().min(1),
  DATABASE_PASSWORD: z.string().min(6),
});

// 自动推导 TypeScript 类型,无需重复编写 interface
export type EnvType = z.infer<typeof envSchema>;
typescript 复制代码
3. 编写验证函数
typescript
运行
// src/config/validate.ts
import { envSchema } from './env.schema';

export function validate(config: Record<string, unknown>) {
  const result = envSchema.safeParse(config);
  if (!result.success) {
    // 格式化错误信息,清晰展示所有不合法项
    const errorList = result.error.issues.map(
      (item) => `[${item.path.join('.')}] ${item.message}`
    );
    throw new Error(`配置验证失败:\n${errorList.join('\n')}`);
  }
  return result.data;
}
javascript 复制代码
4. 模块注册 + 类型化使用
typescript
运行
// src/app.module.ts
import { validate } from './config/validate';

ConfigModule.forRoot({
  isGlobal: true,
  envFilePath: ['.env', `.env.${process.env.NODE_ENV || 'development'}`],
  validate,
});
kotlin 复制代码
typescript
运行
// 业务代码中获得完整类型提示
import { ConfigService } from '@nestjs/config';
import type { EnvType } from '../config/env.schema';

@Injectable()
export class AppService {
  // 第二个泛型设为 true,所有键均非 undefined
  constructor(private configService: ConfigService<EnvType, true>) {}

  getDbConfig() {
    // 自动补全键名,返回值类型精准推导
    const host = this.configService.get('DATABASE_HOST'); // string
    const port = this.configService.get('DATABASE_PORT'); // number
    return { host, port };
  }
}

四、选型速查与最佳实践

项目规模 推荐组合
快速原型 / 小型工具 @nestjs/config + 单 .env 文件
标准业务项目 @nestjs/config + 多环境 .env + Zod/Joi 验证
中大型复杂项目 registerAs 命名空间拆分 + Zod 类型校验 + YAML/.env 分层
企业级配置中心 forRootAsync 异步工厂 + 远程配置拉取 + 全量校验

核心最佳实践

  1. 优先官方体系:以 @nestjs/config 为核心,最大化复用 Nest 生态的注入、验证、模块化能力。
  2. 配置键常量化:通过枚举统一管理键名,消除硬编码魔法字符串,降低维护成本。
  3. 分层配置原则:公共默认配置放基础文件,环境差异化配置放对应环境文件,遵循「兜底 + 覆盖」逻辑。
  4. 强制启动校验:生产环境必须开启配置验证,在启动阶段拦截配置错误。
  5. TS 项目优先 Zod:一次定义实现静态类型与运行时校验双重保障,减少重复代码。
  6. 跨平台兼容:使用 cross-env 统一设置环境变量,规避系统差异。
  7. 敏感配置外置:密钥、密码不提交代码仓库,通过部署平台或配置中心注入。
相关推荐
网络点点滴1 小时前
Node.js事件驱动架构
架构·node.js
王二端茶倒水1 小时前
智慧园区网络运营:认证、分权、运维和安全闭环
运维·物联网·架构
雪隐1 小时前
个人电脑玩AI-03让5060 Ti给你打工——paddleOCR
人工智能·后端
xfhuangfu1 小时前
Oracle 19c 多租户体系架构介绍
数据库·oracle·架构
AskHarries1 小时前
Shell Tool:命令执行、输出读取和长任务管理
后端
苍何1 小时前
开源项目想出海,我让 AI 员工帮我找海外达人
后端
长栎2 小时前
你在 Controller 里注入 8 个 Service,其实是想请一个中介者
后端
小闹5492 小时前
Docker 如何才能学的更扎实
后端·程序员
疯狂的魔鬼2 小时前
多角色督办任务详情页:从权限矩阵到组件拆分的完整实现
前端·vue.js·架构