使用 NestJS 配置环境变量
在现代 web 开发中,环境变量的管理是每个开发者必不可少的一部分。合理地配置环境变量可以帮助开发者在不同的环境下(开发、测试、生产等)灵活地管理应用的配置项。尤其是在使用如 NestJS 这样的框架时,配置和管理环境变量变得尤为重要。NestJS 提供了 nestjs/config 模块,用于集中式管理应用的环境变量,它能够让我们的项目配置更加清晰、可扩展、易于维护。
本文将深入介绍如何在 NestJS 中使用 nestjs/config 来配置环境变量,包括如何配置单一环境变量以及如何动态加载多个环境变量。
一、nestjs/config 模块简介
nestjs/config 是 NestJS 官方提供的一个模块,旨在帮助我们在 NestJS 应用中读取和管理环境变量。它的设计目标是提供一种简单且高效的方式来加载 .env 文件中的变量,并且使得环境变量可以在整个应用中访问。
在实际开发中,很多应用都需要根据不同的部署环境配置不同的参数。例如,开发环境可能需要连接一个本地数据库,而生产环境则需要连接云端数据库。nestjs/config 提供了一种统一的方式来读取这些配置,并且支持通过不同的 .env 文件来适配不同的环境。
二、安装和配置 nestjs/config
首先,确保项目中安装了 nestjs/config 模块。可以通过以下命令安装:
npm install @nestjs/config
安装完成后,我们可以在 AppModule 中导入并配置 ConfigModule,这样就能使得配置模块在整个应用中生效。
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 使得配置在全局范围内可用
}),
],
})
export class AppModule {}
在上述代码中,我们通过 ConfigModule.forRoot() 配置了 nestjs/config,并设置了 isGlobal: true,这意味着配置将对所有模块可用,而不需要在每个模块中单独导入 ConfigModule。
三、配置单一环境变量
在开发中,通常会有一些重要的单一环境变量,比如数据库连接地址、服务端口等。接下来,我们将演示如何配置和使用一个单一的环境变量。
1. 创建 .env 文件
首先,我们需要在项目的根目录下创建一个 .env 文件,用于存储环境变量。例如,在 .env 文件中配置一个数据库连接的 URL:
DATABASE_URL=postgres://user:password@localhost:5432/mydatabase
在 .env 文件中,我们配置了一个名为 DATABASE_URL 的环境变量,值为连接本地 PostgreSQL 数据库的 URL。
2. 使用 ConfigService 读取环境变量
ConfigService 是 nestjs/config 提供的一个服务,用来读取环境变量。在服务中,我们可以通过 ConfigService 来访问 .env 文件中的变量。
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class DatabaseService {
constructor(private configService: ConfigService) {}
getDatabaseUrl() {
const dbUrl = this.configService.get<string>('DATABASE_URL');
console.log('Database URL:', dbUrl);
return dbUrl;
}
}
在这个例子中,ConfigService 通过 get 方法读取 DATABASE_URL 环境变量。使用 ConfigService.get<string>() 时,我们指定了返回值的类型为 string,确保类型安全。
四、配置动态多个环境变量
在复杂的应用中,通常需要配置多个环境变量。例如,我们可能需要配置数据库连接、服务端口、JWT 密钥等。使用 nestjs/config,我们可以轻松地管理这些变量。
1. 动态加载不同的环境配置
我们可以利用envFilePath配合NODE_ENV来,在不同的启动命令的时候使用不同的配置。
npm i cross-env
在开发和生产环境中,我们通常需要使用不同的配置文件。为了实现这一点,我们可以创建多个 .env 文件,如 .env.development、.env.production,然后在启动应用时通过 NODE_ENV 环境变量来指定使用哪个配置文件。
# 开发环境
cross-env NODE_ENV=development nest start --watch
# 生产环境
cross-env NODE_ENV=production node dist/main
然后,我们可以在应用启动时动态加载不同的 .env 文件:
ConfigModule.forRoot({
envFilePath: `.env.${process.env.NODE_ENV}`, // 根据环境变量动态加载配置
isGlobal: true,
}),
2.第二种写法
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
const envPath = `.env.${process.env.NODE_ENV || 'development'}`;
console.log(envPath);
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: envPath,
isGlobal: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
3. 加载多个配置文件
在实际开发中,通常会有多个配置文件用于不同的环境,比如 .env.development、.env.production 等。这些文件可能会有一些共同的配置项,我们可以将这些共同的部分提取到一个 .env 文件中,然后加载多个配置文件来确保所有环境变量的正确性。
假设我们有两个配置文件:一个是 .env 文件,包含共享的基础配置;另一个是根据环境(如 .env.development 或 .env.production)来加载特定配置。
1. 加载多个配置文件示例
在 AppModule 中,我们使用 ConfigModule.forRoot() 来加载这些文件。envFilePath 属性指定要加载的文件路径,我们可以根据当前环境(通过 process.env.NODE_ENV)动态加载相应的配置文件,并且在加载时引入 .env 中的共享配置。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { ConfigModule } from '@nestjs/config';
import * as dotenv from 'dotenv';
// 动态选择加载的环境配置文件
const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 使配置对所有模块可用
envFilePath: envFilePath, // 加载对应环境的配置文件
load: [() => dotenv.config({ path: '.env' })], // 加载共享配置文件
}),
UserModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在这个示例中,envFilePath 会根据 NODE_ENV 的值动态加载 .env.development 或 .env.production 文件。同时,我们还通过 dotenv.config({ path: '.env' }) 加载了 .env 文件中的共享配置。
2. 使用数组加载多个配置文件
除了上述方法,我们还可以直接使用数组的形式指定多个文件路径,这样可以确保同时加载多个配置文件,而不需要手动处理加载顺序。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { ConfigModule } from '@nestjs/config';
const envFilePaths = [
`.env.${process.env.NODE_ENV || 'development'}`, // 根据环境动态加载
'.env', // 加载共享配置文件
];
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 使配置对所有模块可用
envFilePath: envFilePaths, // 加载多个配置文件
}),
UserModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在这种写法中,envFilePath 是一个数组,包含了多个文件路径。NestJS 会依次加载数组中的文件,第一个加载特定环境的配置文件(如 .env.development),第二个加载共享的 .env 文件。
五、使用 Joi 进行环境变量验证
在 NestJS 中,配合 @nestjs/config 使用 Joi 进行环境变量验证是一种非常有效的做法。通过这种方式,你可以确保加载的环境变量符合预期的格式和类型,从而避免潜在的错误。
注意事项:
- 最新版本的
Joi需要 Node.js v12 或更高版本。如果你使用较老版本的 Node.js,请安装v16.1.8,因为v17.0.2及更高版本可能在构建过程中出现错误。详情请参阅 Joi Changelog。 - 推荐将
Joi与官方的@nestjs/config模块一起使用,以简化配置和验证过程。
1. 安装依赖
首先,你需要安装 Joi 依赖:
npm install --save joi
2. 定义验证 Schema
在 AppModule 中,我们可以通过 ConfigModule 配置来加载 .env 文件并进行验证。validationSchema 属性允许你使用 Joi 定义环境变量的验证规则。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import * as Joi from 'joi';
import { ConfigModule } from '@nestjs/config';
const envPath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: envPath,
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test', 'provision')
.default('development'),
PORT: Joi.number().default(3000),
DATABASE_USER: Joi.string().required(),
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在上述代码中:
- 我们定义了一个
Joi.object(),并为每个环境变量指定了验证规则。 NODE_ENV必须是 'development'、'production'、'test' 或 'provision' 中的一个,默认为 'development'。PORT必须是一个数字,默认为3000。DATABASE_USER是必需的,并且必须是字符串类型。
3. 配置验证脚本
我们需要测试一下是否环境变量的验证功能生效。为此,我们可以设置两个 start:dev 脚本来分别测试正确和错误的环境配置。
配置错误的脚本
"start:dev": "cross-env NODE_ENV=development PORT=loser nest start --watch"
在这个脚本中,PORT 被设置为 loser,这是一个非法值。
配置正确的脚本
"start:dev": "cross-env NODE_ENV=development PORT=3000 nest start --watch"
这个脚本使用了一个正确的端口号 3000。
4. 测试验证
你可以通过以下命令运行 NestJS 项目,测试环境变量是否通过 Joi 验证:
npm run start:dev
如果环境变量不符合验证规则(例如 PORT=loser),则应用启动时会抛出一个错误,提示你哪个环境变量不合法。若验证通过,应用将正常启动。
六、总结
在 NestJS 中使用 nestjs/config 来配置环境变量,是管理配置的一个高效方式。通过配置单一环境变量和多个动态加载的环境变量,我们能够灵活地根据不同的环境来调整应用的行为。此外,结合类型验证和环境变量的动态加载,使得我们在开发、测试和生产环境中的配置更加安全和可靠。