Skip to content

在 NestJS 主应用模块中使用环境变量配置数据库连接

在实际开发中,硬编码数据库连接字符串不仅不安全,也不利于多环境部署。本文将详细介绍如何在 NestJS 应用中正确使用环境变量配置数据库连接。

问题分析

在 NestJS 应用中,直接在主模块中使用 ConfigModule.get() 获取环境变量会失败,因为模块加载是同步的,而环境变量加载需要时间。以下代码无法正常工作:

typescript
@Module({
  imports: [
    ConfigModule.forRoot({ envFilePath: `${process.env.NODE_ENV}.env` }),
    MongooseModule.forRoot(`mongodb+srv://${ConfigModule.get('DB_USER')}:${ConfigModule.get('DB_PASS')}@myhost.net?retryWrites=true&w=majority&db=dbname`),
  ],
})

解决方案

方案一:使用异步配置(推荐)

使用 forRootAsync() 方法可以确保在环境变量加载完成后再配置数据库连接:

typescript
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV}`,
    }),
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.get<string>('MONGODB_URI'),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

方案二:创建独立的数据库模块

对于大型项目,建议创建专门的数据库模块:

typescript
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.get<string>('MONGODB_URI'),
        dbName: configService.get<string>('DB_NAME'),
        useNewUrlParser: true,
      }),
      inject: [ConfigService],
    }),
  ],
  exports: [MongooseModule],
})
export class DatabaseModule {}
typescript
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './database/database.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV}`,
    }),
    DatabaseModule,
    // 其他模块...
  ],
})
export class AppModule {}

方案三:在 main.ts 中配置

如果需要在应用启动时使用配置:

typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from '@nestjs/config';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService);
  const port = configService.get('PORT');
  await app.listen(port);
}
bootstrap();

环境配置设置

WARNING

确保正确设置 NODE_ENV 环境变量,否则可能无法加载正确的环境文件。

package.json 中添加脚本:

json
{
  "scripts": {
    "start:dev": "cross-env NODE_ENV=development nest start --watch",
    "start:prod": "cross-env NODE_ENV=production node dist/main"
  }
}

使用 cross-env 包确保跨平台兼容性:

bash
npm install -D cross-env

环境文件示例

创建不同的环境配置文件:

env
MONGODB_URI=mongodb+srv://username:password@localhost:27017/devdb
DB_NAME=devdb
PORT=3000
env
MONGODB_URI=mongodb+srv://username:password@production-host/proddb
DB_NAME=proddb
PORT=80

最佳实践

  1. 安全性:永远不要将敏感信息提交到版本控制系统,使用 .gitignore 忽略环境文件
  2. 默认值:为环境变量提供合理的默认值
  3. 验证:使用 NestJS 的验证功能确保必需的环境变量已设置
typescript
ConfigModule.forRoot({
  validationSchema: Joi.object({
    NODE_ENV: Joi.string()
      .valid('development', 'production', 'test')
      .default('development'),
    PORT: Joi.number().default(3000),
    MONGODB_URI: Joi.string().required(),
  }),
});

总结

通过使用 forRootAsync() 方法和 ConfigService,可以确保在 NestJS 应用中正确使用环境变量配置数据库连接。创建独立的数据库模块和设置正确的环境变量加载顺序是解决此问题的关键。

对于更复杂的配置需求,可以参考 NestJS 官方配置文档获取更多信息。