为 NestJS 应用开发定制化 Config 模块

发布: (2026年2月19日 GMT+8 08:23)
6 分钟阅读
原文: Dev.to

Source: Dev.to

你是否曾经为 比实际功能更多的环境变量 而苦恼?DATABASE_URLSUPABASE_URLJWT_SECRET,还有一些用于本地与生产环境的标志位,甚至还有你承诺以后会清理的“临时”变量。

如果你在代码中到处直接读取 process.env,代码库会很快变得脆弱:

  • 一个拼写错误就会悄悄破坏你的连接。
  • 缺少一个变量会导致应用在运行时崩溃。
  • 你最终会在调试配置上浪费时间,而不是交付功能。

如果有一种更好、更简洁、更专业的方式来处理这些混乱该多好?在本文中,我们将构建一个 小巧、类型安全、经过验证的 Config Module,它将成为本系列其余部分的基础。

为什么使用自定义 Config 模块?

Nest 已经提供了 @nestjs/config,它非常好用。问题在于大多数教程只停留在“安装它,然后大功告成”。

对于生产级别的 API,我们需要更多:

  • 一个统一的地点 来加载和验证环境变量
  • 清晰的命名空间appdb(暂时)
  • 类型推断,使配置访问安全且易于发现
  • 快速失败 的验证(在应用启动前)

这在我们的个人理财 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

0 浏览
Back to Blog

相关文章

阅读更多 »