为 NestJS 应用开发定制化 Config 模块
Source: Dev.to
你是否曾经为 比实际功能更多的环境变量 而苦恼?DATABASE_URL、SUPABASE_URL、JWT_SECRET,还有一些用于本地与生产环境的标志位,甚至还有你承诺以后会清理的“临时”变量。
如果你在代码中到处直接读取 process.env,代码库会很快变得脆弱:
- 一个拼写错误就会悄悄破坏你的连接。
- 缺少一个变量会导致应用在运行时崩溃。
- 你最终会在调试配置上浪费时间,而不是交付功能。
如果有一种更好、更简洁、更专业的方式来处理这些混乱该多好?在本文中,我们将构建一个 小巧、类型安全、经过验证的 Config Module,它将成为本系列其余部分的基础。
为什么使用自定义 Config 模块?
Nest 已经提供了 @nestjs/config,它非常好用。问题在于大多数教程只停留在“安装它,然后大功告成”。
对于生产级别的 API,我们需要更多:
- 一个统一的地点 来加载和验证环境变量
- 清晰的命名空间 如
app和db(暂时) - 类型推断,使配置访问安全且易于发现
- 快速失败 的验证(在应用启动前)
这在我们的个人理财 API 中尤为重要,因为后续步骤依赖于稳定的配置:
- Drizzle 需要一个有效的 Postgres 连接字符串
- 本地和生产环境必须表现一致可预测
创建模块
我们将创建一个 全局 模块,作为配置的唯一可信来源。
生成一个配置模块(自行选择路径):
nest g mo config
现在使用 Nest 的 ConfigModule 设置该模块,但仍将其封装在你自己的模块中。
// config/config.module.ts
import { Global, Module } from '@nestjs/common';
import { ConfigModule as NestConfigModule } from '@nestjs/config';
@Global()
@Module({
imports: [
NestConfigModule.forRoot({
isGlobal: true,
cache: true,
envFilePath: [`.env.${process.env.NODE_ENV}`, '.env'],
}),
],
exports: [NestConfigModule],
})
export class ConfigModule {}
这已经为我们提供了:
- 全局配置(无需在每处都导入)
- 环境特定的
.env加载 - 为提升性能而进行的缓存
定义配置命名空间(app,db)
为了避免在代码库中到处散落变量名,我们将创建配置“命名空间”。这样可以保持结构清晰,也让后续的文章更易于跟进。
示例:app 配置
// config/configurations/app.config.ts
import { registerAs } from '@nestjs/config';
export default registerAs(
'app',
(): AppConfig => ({
port: parseInt(process.env.PORT || '3000', 10),
nodeEnv: (process.env.NODE_ENV || 'development') as AppConfig['nodeEnv'],
}),
);
// config/types/app-config.types.ts
export type AppConfig = {
port: number;
nodeEnv: 'development' | 'production' | 'test' | 'staging';
};
在本系列中,关键点在于,当我们连接 Drizzle 时,能够读取类似 db.connectionString 的配置。
使用 Joi 验证环境变量
类型安全很好,但 验证 才能防止错误的配置进入生产环境。我们将在应用启动前使用 Joi 来验证环境变量。
安装 Joi:
pnpm i joi
创建验证模式:
// config/validations/env.validation.ts
import * as Joi from 'joi';
export const envValidationSchema = Joi.object({
PORT: Joi.number().default(3000),
NODE_ENV: Joi.string()
.valid('development', 'production', 'test', 'staging')
.default('development'),
});
将所有内容组合在一起:
// config/config.module.ts
import { Global, Module } from '@nestjs/common';
import { ConfigModule as NestConfigModule } from '@nestjs/config';
import appConfig from './configurations/app.config';
import { envValidationSchema } from './validations/env.validation';
@Global()
@Module({
imports: [
NestConfigModule.forRoot({
isGlobal: true,
cache: true,
envFilePath: [`.env.${process.env.NODE_ENV}`, '.env'],
load: [appConfig],
validationSchema: envValidationSchema,
}),
],
exports: [NestConfigModule],
})
export class ConfigModule {}
快速测试
创建一个包含您定义的变量的 .env 文件并启动应用程序。如果有任何变量缺失或无效,Nest 将快速失败并准确告诉您哪里出错。这就是重点。
如果您在控制台看到 Nest application successfully started,说明您已经成功了。
总结
此时,你已经构建了一个配置层,它具备以下特性:
- 集中式(单一模块)
- 类型安全(结构化配置对象)
- 已验证(Joi 模式)
- 已准备好进行下一步(Drizzle + Supabase 集成)
在下一篇文章中,我们将使用该模块来使用 Supabase Postgres 连接字符串初始化 Drizzle,并开始定义我们的模式。但要做好准备,我们需要数据库配置——这就是你的作业。下篇见!
💡 下一篇:使用 Drizzle 和我们的配置模块将 Supabase Postgres 连接到 NestJS。
🔗 代码: https://github.com/RubenOAlvarado/finance-api/tree/v0.2.0