{T}

NestJS配置模块学习笔记

NestJS配置模块学习笔记

核心知识点

1. @nestjs/config 概述

1.1 什么是 @nestjs/config?

@nestjs/config 是 NestJS 官方提供的配置管理模块,其底层内置了 dotenv 库(周下载量极高),负责解析 .env 文件中的键值对,并将其封装为 NestJS 的服务模块。

code
@nestjs/config
    └── 内置 dotenv(解析 .env 文件)
        └── 将键值对挂载到 process.env

1.2 课程资料说明

课程资料文件夹中的文件命名规则:

文件后缀含义用途
*-start初始代码新学员从该文件开始跟着练习
*-end(无后缀)最终代码学习完成后对比查看配置问题

2. 安装与版本管理

2.1 安装模块

bash
pnpm install @nestjs/config

2.2 版本锁定

如果安装的版本大版本号与课程不一致(如当前课程版本为 2.2.x),建议锁定版本号安装,避免兼容性问题:

bash
pnpm install @nestjs/config@2

2.3 验证安装

安装完成后,在 package.json 中确认依赖已正确添加:

json
{
  "dependencies": {
    "@nestjs/config": "^2.2.0"
  }
}

3. 创建 .env 配置文件

3.1 .env 文件格式

在项目根目录创建 .env 文件,采用**键值对(Key-Value)**形式:

env
DB=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=123456

3.2 注意事项

  • .env 文件应加入 .gitignore,避免敏感信息提交到代码仓库
  • 键名之间不要有空格DB=mysql / DB = mysql
  • 修改 .env 文件后需要重启应用才能生效

4. ConfigModule 注册与使用

4.1 在 AppModule 中全局注册(推荐)

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

@Module({
  imports: [
    // 全局注册配置模块  推荐
    ConfigModule.forRoot({
      isGlobal: true,  // 关键:设为全局模块
    }),
    UserModule,
  ],
})
export class AppModule {}

forRoot():读取根目录下的 .env 文件并加载配置。

isGlobal: true:将 ConfigModule 标记为全局模块,所有子模块可直接注入使用,无需重复导入。

4.2 在 Controller 中注入使用

typescript
// user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.enum';

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

  @Get()
  getUsers() {
    // 方式一:直接使用字符串(不推荐)
    console.log(this.configService.get('DB'));

    // 方式二:使用枚举(推荐)
    console.log(this.configService.get(ConfigEnum.DB));
    console.log(this.configService.get(ConfigEnum.DB_HOST));

    return { users: [] };
  }
}

5. 跨模块使用配置

5.1 问题:非全局模式下跨模块报错

如果不设置 isGlobal: true,在 UserModule 中直接注入 ConfigService 会报错:

code
Nest can't resolve dependencies of the UserController (ConfigService, ?).
Please make sure that the argument UserService is available in the UserModule context.

原因ConfigModule 未在 UserModule 中导入,ConfigService 无法被解析。

5.2 非全局模式的解决方案(不推荐)

需要在每个需要使用配置的模块中单独导入:

typescript
// user.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UserController } from './user.controller';

@Module({
  imports: [
    // 每个模块都要导入  繁琐
    ConfigModule.forRoot(),
  ],
  controllers: [UserController],
})
export class UserModule {}

5.3 两种方式对比

方式代码优点缺点
isGlobal: trueAppModule 设置一次所有模块自动可用,代码简洁全局可见
逐模块导入在每个模块单独导入作用域可控代码重复,维护成本高

推荐:在大多数业务项目中使用 isGlobal: true,因为配置本身就是全局性的。


6. 使用枚举管理配置键名

6.1 为什么使用枚举?

直接使用字符串存在以下风险:

typescript
//  硬编码字符串:修改 .env 变量名时容易遗漏
this.configService.get('DB');

//  枚举方式:修改一处即可全局生效
this.configService.get(ConfigEnum.DB);

.env 文件中的变量名发生变更时(如 DBDB1),只需修改枚举定义即可,所有引用处自动更新。

6.2 创建配置枚举

typescript
// enum/config.enum.ts(或 enums/config.ts)
export enum ConfigEnum {
  DB = 'DB',
  DB_HOST = 'DB_HOST',
  DB_PORT = 'DB_PORT',
  DB_USERNAME = 'DB_USERNAME',
  DB_PASSWORD = 'DB_PASSWORD',
}

6.3 使用枚举

typescript
// user.controller.ts
import { ConfigEnum } from '../enum/config.enum';

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

  @Get()
  getUsers() {
    const db = this.configService.get(ConfigEnum.DB);         // 'mysql'
    const host = this.configService.get(ConfigEnum.DB_HOST);   // '127.0.0.1'
    return { db, host };
  }
}

6.4 枚举维护流程

code
1. 修改 .env 文件中的变量名(如 DB → DB1)
2. 同步修改 ConfigEnum 中的枚举值
3. 所有使用 ConfigEnum 的地方自动生效
4. 重启应用

代码实战案例

需求描述

在 NestJS 项目中配置数据库连接信息,使用 @nestjs/config 全局加载 .env 配置,并通过枚举在 Controller 中安全访问。

完整实现

项目结构

code
src/
├── app.module.ts          # 根模块,全局注册 ConfigModule
├── enum/
│   └── config.enum.ts     # 配置键名枚举
├── user/
│   ├── user.module.ts     # 用户模块
│   └── user.controller.ts # 用户控制器,注入 ConfigService
├── .env                   # 环境变量文件
└── main.ts                # 入口文件

第一步:创建 .env 文件

env
# .env
DB=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=123456

第二步:定义配置枚举

typescript
// src/enum/config.enum.ts
export enum ConfigEnum {
  DB = 'DB',
  DB_HOST = 'DB_HOST',
  DB_PORT = 'DB_PORT',
  DB_USERNAME = 'DB_USERNAME',
  DB_PASSWORD = 'DB_PASSWORD',
}

第三步:在 AppModule 中全局注册

typescript
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { UserModule } from './user/user.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 全局模块
    }),
    UserModule,
  ],
})
export class AppModule {}

第四步:在 Controller 中使用

typescript
// src/user/user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.enum';

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

  @Get()
  getUsers() {
    const dbConfig = {
      type: this.configService.get(ConfigEnum.DB),
      host: this.configService.get(ConfigEnum.DB_HOST),
      port: this.configService.get<number>(ConfigEnum.DB_PORT),
      username: this.configService.get(ConfigEnum.DB_USERNAME),
    };

    console.log('数据库配置:', dbConfig);
    return { users: [], dbConfig };
  }
}

第五步:启动并测试

bash
pnpm start:dev

浏览器访问 http://localhost:3000/api/v1/user,终端输出:

code
数据库配置: {
  type: 'mysql',
  host: '127.0.0.1',
  port: 3306,
  username: 'root'
}

常见问题与解决方案

问题原因解决方案
ConfigService 注入报错未在当前模块导入 ConfigModule设置 isGlobal: true 或在对应模块导入
修改 .env 后配置未更新dotenv 只在启动时加载一次重启应用(Ctrl+C 后重新启动)
获取到的值为 undefined.env 中的键名与代码中不一致检查键名拼写,使用枚举避免此类问题
版本不兼容导致 API 差异安装了不同大版本的包使用 pnpm install @nestjs/config@2 锁定版本
TypeScript 类型提示缺失configService.get() 返回 any使用泛型 get<number>('DB_PORT') 指定类型

学习要点总结

  1. @nestjs/config 是 NestJS 官方配置模块,底层使用 dotenv 解析 .env 文件
  2. ConfigModule.forRoot({ isGlobal: true }) 将配置设为全局可用,避免每个模块重复导入
  3. ConfigService.get() 用于读取配置值,支持泛型指定返回类型
  4. 使用枚举管理配置键名,提升代码可维护性,修改时只需改一处
  5. 修改 .env 后必须重启应用,因为 dotenv 仅在应用启动时加载一次

延伸学习资源

官方文档

后续课程预告

  • 配置校验:使用 Joiclass-validator.env 配置进行类型和必填校验
  • 多环境配置.env.development.env.production 等多文件切换方案
  • 命名空间配置:按模块拆分配置文件的进阶用法

最佳实践

  • 敏感信息(密码、密钥)绝不提交到 Git 仓库
  • 使用 .env.example 作为配置模板提交,不含实际值
  • 结合枚举 + TypeScript 泛型,实现类型安全的配置读取