NestJS 配置环境变量

464 阅读7分钟

使用 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 读取环境变量

ConfigServicenestjs/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 来配置环境变量,是管理配置的一个高效方式。通过配置单一环境变量和多个动态加载的环境变量,我们能够灵活地根据不同的环境来调整应用的行为。此外,结合类型验证和环境变量的动态加载,使得我们在开发、测试和生产环境中的配置更加安全和可靠。