Skip to content

malyshev/nestjs-async-validator-demo

Repository files navigation

Async Validator with Dependency Injection in NestJS

This repository demonstrates how to properly inject services into class-validator custom constraints in a NestJS project — a common pitfall with a surprisingly simple but non-obvious fix.

🔍 Problem

By default, class-validator doesn't know about NestJS’s dependency injection system. So if you try to inject a service into an @ValidatorConstraint() class, it simply won't work:

@ValidatorConstraint({ async: true })
@Injectable()
export class UniqueUsernameValidator implements ValidatorConstraintInterface {
  constructor(private readonly userService: UsersService) {}

  async validate(username: string): Promise<boolean> {
    return !(await this.userService.findByUsername(username));
  }
}

Even though UsersService is a valid NestJS provider, the validator will throw a runtime error — unless you take one crucial step:

✅ Solution

Before running your app, tell class-validator to use Nest’s DI container:

import { useContainer } from 'class-validator';

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

  useContainer(app.select(AppModule), { fallbackOnErrors: true });

  await app.listen(3000);
}

Without this, class-validator will instantiate constraint classes using its own logic and ignore your providers.

📦 How to Run

npm install
npm run start

App will be available at: http://localhost:3000

🧪 Test the Validator

Use curl or Postman to test the /users endpoint.

✅ Successful Request (username is available):

curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"username": "new-username"}'

❌ Error (username is taken):

curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"username": "taken-username"}'

Response:

{
  "message": [
    "username is already taken"
  ],
  "error": "Bad Request",
  "statusCode": 400
}

📂 Folder Structure

src/
  app.module.ts
  main.ts
  users/
    users.controller.ts
    users.service.ts
    users.module.ts
    dto/
      create-user.dto.ts
    validators/
      unique-username.validator.ts

🧠 Bonus Tip

The validator won’t work unless:

  • It’s decorated with @ValidatorConstraint({ async: true })
  • It’s provided as a NestJS provider (in app.module.ts)
  • You call useContainer(app.select(AppModule)) in main.ts

🙌 Inspired by Real Pain™

This issue was the source of real-world confusion and wasted hours. Now it’s documented and fixed — hope it helps someone!

Article: The Hidden Fix Behind NestJS Async Validators That Nobody Tells You About

About

Async Validator with Dependency Injection in NestJS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published