清理你的 NestJS 控制器:在 DTO 中进行远程 API 验证 🧼🚀

发布: (2025年12月7日 GMT+8 03:31)
4 min read
原文: Dev.to

Source: Dev.to

清理你的 NestJS 控制器:DTO 中的远程 API 验证 🧼🚀

“远程验证” 的困扰

如果你使用 NestJS,可能已经爱上了 class-validator。在属性上加上 @IsEmail() 装饰器,然后安心睡觉,因为错误数据不会进入你的服务,这种感觉非常棒。

但当我们需要 远程验证 时,这种安心感往往会消失。

你一定遇到过这样的场景:

  • 前端发送 userIdcouponCodetransactionId
  • 你需要调用外部微服务或 API 来检查它是否有效。
  • 如果有效,通常还需要获取额外的数据(比如用户的姓名)并保存到本地。

“脏” 方法(我们都做过)

// ❌ The common anti-pattern
@Post()
async create(@Body() dto: CreateOrderDto) {
  // Validation logic leaking into the Controller/Service
  const response = await this.http.get(`https://external-api.com/users/${dto.userId}`);

  if (response.status !== 200) {
    throw new BadRequestException('Invalid User ID');
  }

  // Business logic finally starts here...
}

这违背了 NestJS 的约定。控制器应该只处理请求,服务层负责业务逻辑,验证应当停留在入口——DTO 中。

解决方案:nestjs-remote-validate

我厌倦了写重复的样板代码,于是构建了一个库,能够在 DTO 装饰器内部进行 HTTP 验证

// ✅ The clean way
export class CreateOrderDto {
  @ValidateApi({
    host: 'https://external-api.com/users/:userId', // Dynamic injection!
    method: 'GET',
    validate: ({ status }) => status === 200,
  })
  userId: string;
}

杀手级特性:DTO 丰富化 🐱

很多时候你不仅想验证,还想 使用 API 返回的数据。该库允许你从响应中提取数据并自动注入到 DTO 的另一个字段。

export class CreateTransactionDto {
  // 1. Validates if the ID exists remotely
  @ValidateApi({
    host: 'https://bank-api.com/accounts/:accountId',
    method: 'GET',
    validate: ({ status }) => status === 200,
    // 2. Extracts the owner name from the response body
    extractValue: (body) => body.ownerName,
    // 3. Injects it into the field below
    targetField: 'accountOwnerName',
  })
  accountId: string;

  // This field gets populated "magically" after validation!
  @Allow()
  accountOwnerName: string;
}

当 DTO 抵达控制器时,accountOwnerName 已经被填充。服务代码中不再需要任何 HTTP 调用。 🤯

快速开始

1. 安装

npm install nestjs-remote-validate

2. 配置(重要!)

验证器需要执行异步操作并进行注入,所以要注册 provider 并告诉 class-validator 使用 NestJS 容器。

app.module.ts

import { ApiValidatorConstraint } from 'nestjs-remote-validate';

@Module({
  providers: [ApiValidatorConstraint], // Register the provider
})
export class AppModule {}

main.ts

import { useContainer } from 'class-validator';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Allow dependency injection inside validators
  useContainer(app.select(AppModule), { fallbackOnErrors: true });

  app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
  await app.listen(3000);
}
bootstrap();

当前限制与路线图(一起构建!)

  • HTTP 客户端:目前使用原生 fetch。后续计划引入 Axios 注入,以复用全局拦截器(日志、认证等)。
  • 缓存:每次验证都会发起请求。实现 缓存策略(内存/Redis)可以提升重复验证的性能。
  • 批量请求:一次调用即可验证 ID 数组。

这些都是 “Good First Issues” 或者稳健 PR 的绝佳机会。

贡献! 🤝

如果这个库解决了你的痛点,欢迎尝试!对 Axios 支持或 缓存 的想法非常欢迎——直接在仓库里提出。每一个 star、issue 或 PR 都会让生态系统更健康。

  • GitHub:
  • NPM:

祝编码愉快! 🚀

Back to Blog

相关文章

阅读更多 »