如何处理环境变量
node.js中有一个专门的 npm 包 dotenv 用来处理环境变量。
nest.js 对它进行封装,提供了一个内置包 @nest/config。按照官方文档,使用已经封装好的ConfigModule来处理环境配置文件。
首先安装依赖:
bash1pnpm i --save @nestjs/config
在app.module.ts中导入ConfigModule模块。
typescript1import { Module } from '@nestjs/common' 2import { AppController } from './app.controller' 3import { AppService } from './app.service' 4import { ConfigModule } from '@nestjs/config' 5 6@Module({ 7 imports: [ConfigModule.forRoot()], 8 controllers: [AppController], 9 providers: [AppService], 10}) 11export class AppModule {}
env 文件的环境变量
创建.env 文件
yaml1DATABASE_USER=test
在app.service.ts中注入ConfigService并测试一下:
typescript1import { Inject, Injectable } from '@nestjs/common' 2import { ConfigService } from '@nestjs/config' 3 4@Injectable() 5export class AppService { 6 constructor( 7 @Inject(ConfigService) 8 private readonly configService: ConfigService, 9 ) {} 10 getHello() { 11 const a = this.configService.get('DATABASE_USER') 12 return { a } 13 } 14}
访问一下可以看到结果:

ConfigService会自动提取.env文件中的环境变量。
那如果我有多个环境变量的文件该怎么办?
再创建.env.dev文件
yaml1DATABASE_PASSWORD=test_password
这样就得手动指定.env文件的 path 了
typescript1 ConfigModule.forRoot({ 2 envFilePath: ['.env.dev', '.env'], 3 }),
@nestjs/config会将多个文件的环境变量合并到一起。
修改一下代码:
typescript1 getHello() { 2 const a = this.configService.get('DATABASE_USER'); 3 const b = this.configService.get('DATABASE_PASSWORD'); 4 return { a, b }; 5 }
再访问一下:

接下来还有一个问题,如果线上分为test环境、prod环境,并且这些环境的环境变量的值是不一样的,该如何处理?
我们再新建.env.test和.env.prod两个文件。
其中.env.test的环境变量是这样的:
yaml1APP_ENV=test
.env.prod的环境变量是这样的:
yaml1APP_ENV=prod
这种情况就需要结合一个外部的NODE_ENV来实现了。
在package.json中新增两条脚本:
json1 "scripts": { 2 "dev:test":"NODE_ENV=test nest start --watch", 3 "dev:prod":"NODE_ENV=prod nest start --watch", 4 ... 5 },
这两个脚本的意思是在执行该脚本时,将NODE_ENV设置为test或者prod。
接着修改 envFilePath:
typescript1 ConfigModule.forRoot({ 2 envFilePath: ['.env.dev', '.env', `.env.${process.env.NODE_ENV}`], 3 }),
现在envFilePath会再根据NODE_ENV的值去读取对应的环境变量文件。
如果执行pnpm dev:test,就会将.env.test文件给合并进来。
修改代码测试一下:
typescript1 getHello() { 2 const a = this.configService.get('DATABASE_USER'); 3 const b = this.configService.get('DATABASE_PASSWORD'); 4 const appEnv = this.configService.get('APP_ENV'); 5 return { a, b, appEnv }; 6 }
查看结果:
如果执行的是pnpm dev:prod,就会将.env.prod文件合并进来。
查看结果:

ts 文件的环境变量
使用.env文件配置环境变量不够灵活,能否用 ts 代码来生成环境变量?
@nestjs/config 也是支持的。
创建config.ts
typescript1export default async () => { 2 const dbPort = await Promise.resolve(5432) 3 const dbHost = await Promise.resolve('localhost') 4 return { 5 db: { 6 host: dbHost, 7 port: dbPort, 8 }, 9 } 10}
使用 load 加载 config.ts 导出的方法。
typescript1... 2import config from './config'; 3 4@Module({ 5 imports: [ 6 ConfigModule.forRoot({ 7 envFilePath: ['.env.dev', '.env', `.env.${process.env.NODE_ENV}`], 8 load: [config], 9 }), 10 ], 11... 12}) 13...
修改代码:
typescript1 getHello() { 2 const a = this.configService.get('DATABASE_USER'); 3 const b = this.configService.get('DATABASE_PASSWORD'); 4 const appEnv = this.configService.get('APP_ENV'); 5 const db = this.configService.get('db'); 6 return { a, b, appEnv, db }; 7 }
最后执行pnpm dev:prod并查看结果:

通过这种方式可以动态加载环境变量。
设置 global service
创建一个resource
bash1nest generate resource bbb --no-spec
在 Controller 中注入 ConfigService
typescript1import { Controller, Get } from '@nestjs/common' 2import { ConfigService } from '@nestjs/config' 3 4@Controller('bbb') 5export class BbbController { 6 constructor(private readonly configService: ConfigService) {} 7 8 @Get('/') 9 async getHello() { 10 return this.configService.get('db') 11 } 12}
会得到一个报错:

原因是现在的ConfigService还不是全局模块,仅仅只能在AppModule中使用。
将其修改为全局模块:
diff1 ConfigModule.forRoot({ 2 envFilePath: ['.env.dev', '.env', `.env.${process.env.NODE_ENV}`], 3 load: [config], 4+ isGlobal: true, 5 }),
现在访问一下http://localhost:3000/bbb看看结果:

forFeature
nestjs约定, 动态模块的forRoot方法通常用于在 AppModule 里注册,一般用于指定全局模块。
forFeature 用于局部配置,在不同模块里 imports。
而 register 用于一次性的配置。
如果我们想要在 bbbModule 中使用局部环境变量,则可以使用forFeature方法:
typescript1 imports: [ 2 ConfigModule.forFeature(() => { 3 return { 4 bbb: 'bbbbbb', 5 }; 6 }), 7 ],
修改代码测试一下:
typescript1 @Get('/') 2 async getHello() { 3 // return this.configService.get('db'); 4 return this.configService.get('bbb'); 5 }
访问http://localhost:3000/bbb查看结果:

总结
Nodejs 使用 dotenv 读取环境变量。
Nestjs 对其进行封装提供了@nestjs/config包。
使用 ConfigModule.forRoot 可以读取 .env 配置文件,然后注入 ConfigService 来取配置。
还可以通过 ConfigModule.forFeature 来注册局部配置。