Clean up your NestJS Controller: Remote API Validation inside DTOs 🧼🚀

Published: (December 6, 2025 at 02:31 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Cover image for Clean up your NestJS Controller: Remote API Validation inside DTOs 🧼🚀

The “Remote Validation” Struggle

If you work with NestJS, you probably love class-validator. Putting an @IsEmail() decorator on a property and sleeping soundly knowing bad data won’t reach your service is a great feeling.

But that peace of mind usually vanishes when we need Remote Validation.

You know the scenario:

  • The frontend sends a userId, couponCode, or transactionId.
  • You need to hit an external microservice or API to check if it’s valid.
  • If it’s valid, you often need to fetch extra data (like the user’s name) to save it locally.

The “Dirty” Way (We’ve all done this)

// ❌ 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...
}

This breaks NestJS conventions. Controllers should handle requests, services should contain business logic, and validation should stay at the gate—in the DTO.

The Solution: nestjs-remote-validate

I got tired of writing boilerplate code, so I built a library that performs HTTP validation inside DTO decorators.

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

The Killer Feature: DTO Enrichment 🐱

Often you don’t just want to validate; you want to use the data returned by the API. The library lets you extract data from the response and automatically inject it into another field of your 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;
}

By the time the DTO reaches your controller, accountOwnerName is already filled. Zero HTTP calls in your service code. 🤯

Quick Start

1. Install

npm install nestjs-remote-validate

2. Setup (Important!)

The validator needs to perform async operations and injection, so register the provider and tell class-validator to use the NestJS container.

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();

Current Limitations & Roadmap (Let’s Build This!)

  • HTTP Client: Currently uses native fetch. Future plans include Axios injection to reuse global interceptors (logging, auth, etc.).
  • Caching: Every validation triggers a request. Implementing a caching strategy (memory/Redis) would boost performance for repeated validations.
  • Batching: Validate arrays of IDs in a single call.

These are great opportunities for “Good First Issues” or robust PRs.

Contribute! 🤝

If the library solves a pain point for you, give it a try! Ideas for Axios support or caching are welcome—drop by the repository. Every star, issue, or PR helps the ecosystem grow.

  • GitHub:
  • NPM:

Happy coding! 🚀

Back to Blog

Related posts

Read more »