NestJS 环境变量配置读取技巧
在 NestJS 项目中,环境配置管理是一个重要话题。本文介绍如何优雅地管理环境变量、配置文件,以及如何进行验证。
一、环境变量基础
1.1 使用 @nestjs/config
NestJS 提供了 @nestjs/config 包来管理配置:
bash
pnpm install @nestjs/config1.2 基础配置
typescript
// app.module.ts
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 全局可用,无需重复导入
}),
],
})
export class AppModule {}1.3 在代码中读取环境变量
typescript
import { ConfigService } from '@nestjs/config';
@Injectable()
export class UserService {
constructor(private configService: ConfigService) {}
getDbUrl() {
return this.configService.get<string>('DATABASE_URL');
}
}二、使用 .env 文件
2.1 安装依赖
bash
pnpm install dotenv2.2 环境文件示例
bash
# .env
DB_HOST=127.0.0.1
DB_PORT=3306
DB_URL=www.example.com
# .env.development
DB=mysql-dev
DB_HOST=127.0.0.1
DB_URL=www.imooc.com
# .env.production
DB=mysql-prod
DB_HOST=192.168.1.1
DB_URL=www.imooc-prod.com2.3 配置加载多个 .env 文件
typescript
// app.module.ts
const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath, // 指定环境文件
}),
],
})2.4 多文件加载与优先级
优先级:靠前的文件优先级更高
typescript
// 数组形式,前面的优先级高
const envFilePath = [
`.env.${process.env.NODE_ENV || 'development'}`,
'.env'
];
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath,
}),
],
})加载顺序示例(development 环境):
.env.development- 最高优先级.env- 作为默认值补充
三、使用 YAML 配置文件
3.1 安装依赖
bash
pnpm install js-yaml
pnpm install -D @types/js-yaml3.2 YAML 配置示例
yaml
# config/default.yaml
token_secret: long-secret
db:
host: "localhost"
port: 5432
# config/development.yaml
db:
host: "dev-local"
port: 3306
# config/production.yaml
db:
host: "prod-server"
port: 54323.3 加载 YAML 配置
typescript
import { ConfigModule } from '@nestjs/config';
import * as yaml from 'js-yaml';
import * as fs from 'fs';
const getConfig = () => {
const env = process.env.NODE_ENV || 'development';
const defaultConfig = yaml.load(
fs.readFileSync('config/default.yaml', 'utf8')
);
const envConfig = yaml.load(
fs.readFileSync(`config/${env}.yaml`, 'utf8')
);
return { ...defaultConfig, ...envConfig };
};
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [getConfig],
}),
],
})四、使用 Enum 定义变量名
使用枚举可以避免硬编码字符串,提供类型安全:
4.1 定义枚举
typescript
// enum/config.enum.ts
export enum ConfigEnum {
DB = 'DB',
DB_HOST = 'DB_HOST',
DB_PORT = 'DB_PORT',
DB_URL = 'DB_URL',
}4.2 使用枚举读取
typescript
import { ConfigEnum } from './enum/config.enum';
@Injectable()
export class UserService {
constructor(private configService: ConfigService) {}
getDbHost() {
return this.configService.get<string>(ConfigEnum.DB_HOST);
}
getDbUrl() {
return this.configService.get<string>(ConfigEnum.DB_URL);
}
}五、使用 cross-env 设置环境
5.1 安装依赖
bash
pnpm install -D cross-env5.2 配置启动脚本
json
{
"scripts": {
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main"
}
}为什么需要 cross-env?
跨平台设置环境变量:
- Windows:
set NODE_ENV=production - Unix/Mac:
NODE_ENV=production - cross-env 统一了两种平台的差异
六、使用 Joi 验证环境变量
6.1 安装依赖
bash
pnpm install joi6.2 配置验证规则
typescript
import * as Joi from 'joi';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: [`.env.${process.env.NODE_ENV}`, '.env'],
validationSchema: Joi.object({
// 环境必须是 development 或 production
NODE_ENV: Joi.string()
.valid('development', 'production')
.default('development'),
// 端口必须是数字,默认 3306
DB_PORT: Joi.number().default(3306),
// URL 必须是合法域名
DB_URL: Joi.string().domain(),
// HOST 必须是合法 IP
DB_HOST: Joi.string().ip(),
// 必填项
API_KEY: Joi.string().required(),
}),
}),
],
})6.3 验证时机
验证在应用启动时进行,验证失败会导致应用启动失败,提前发现配置问题。
七、完整配置示例
typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
// 靠前的元素优先级高
const envFilePath = [
`.env.${process.env.NODE_ENV || 'development'}`,
'.env'
];
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath,
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production')
.default('development'),
DB_PORT: Joi.number().default(3306),
DB_URL: Joi.string().domain(),
DB_HOST: Joi.string().ip(),
}),
}),
UserModule,
],
})
export class AppModule {}八、配置优先级总结
从高到低的优先级顺序:
1. 环境变量 (process.env)
2. .env.${NODE_ENV} 文件
3. .env 文件
4. load() 函数加载的配置
5. 默认值
九、最佳实践
| 实践 | 说明 |
|---|---|
.env 加入 .gitignore | 避免敏感信息泄露 |
.env.example 提供模板 | 让其他人知道需要哪些环境变量 |
| 使用 Joi 验证 | 启动时发现配置问题 |
| 使用 Enum | 避免硬编码,提供类型安全 |
| 使用 cross-env | 跨平台兼容 |
isGlobal: true | 避免每个模块都导入 ConfigModule |
十、常见问题
Q: 为什么我的环境变量读取不到?
A: 检查以下几点:
.env文件是否在项目根目录envFilePath路径是否正确- 环境变量名是否拼写一致
Q: 如何在 main.ts 中使用配置?
A: 可以使用 app.get(ConfigService) 或直接使用 process.env:
typescript
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = app.get(ConfigService).get('PORT', 3000);
await app.listen(port);
}