Custom validation with database in NestJS
Tags: nestjs, typescript, node, javascript
Published: 2021-03-27 17:33:17

NestJS is an outstanding web framework which supports natively TypeScript out of the box. Its maintainers and community provides great and large documentation, which leads us by hand through the most important parts of the framework.

But when you start to write an application for serious, you figure out quickly, it doesn't coverage some very typical cases (at least for me).

Framework provides several ways for validation data coming with requests. We can basically use **_Pipes_**, a feature called **_schema-based_** validation (using [joi library](https://joi.dev/)) or integration with **_class-validator_** library through `ValidatorPipe`. And the last one is my favorite one. Why? The main reason is that you can keep your whole validation definitions outside the controller. It's a great way to separate different concerns.

[Class-Validator library](https://github.com/typestack/class-validator) is a powerful tool, which brings whole set of differential validation decorators like `@Length(10, 20)`, `@IsInt()`, `@Contains('example')` etc. I won't introduce to you how to use basic validation in NestJS, documentation explains it quite accurately.

But what if you would like to create your own validator, and use it with class-validator library? Easy, just quick look at [the documentation](https://github.com/typestack/class-validator#custom-validation-classes) and you can write your own rules and can use it with `@Validate()` decorator. Better! It's fabulously simple to write own decorators and have your whole request validation class consists.

Problems start when we are forced to check something for example in persistent storage, like database. In short – we must inject some dependency responsible for interacting with the database. Dependency like e.g. `UserRepository` which is obviously responsible for users entities.

Luckly for us, `class-validator` provides very handy `useContainer` function, which allow to set the container to be used by `class-validor` library.

So add this code in your main.ts file (`app` variable is your Nest application instance):

```TypeScript
useContainer(app.select(AppModule), { fallbackOnErrors: true });
```
It allows `class-validator` to use NestJS dependency injection container.

Then we can create a repository, which will query our database:

```TypeScript
@Injectable()
class UserRepository {
  async getOneOrFail(userId: number): Promise<UserEntity> {
    // some code which fetch user entity or throw exception
  }
}
```
Ok, let's write a `Validator Constraint` which will hold our own validation logic. As you can see, our dependency is simply injected into the class constructor:

```TypeScript
@ValidatorConstraint({ name: 'UserExists', async: true })
@Injectable()
export class UserExistsRule implements ValidatorConstraintInterface {
  constructor(private usersRepository: UsersRepository) {}

  async validate(value: number) {
    try {
      await this.usersRepository.getOneOrFail(value);
    } catch (e) {
      return false;
    }

    return true;
  }

  defaultMessage(args: ValidationArguments) {
    return `User doesn't exist`;
  }
}
```
Don't forget to [declare you injectable classes as providers](https://docs.nestjs.com/providers#scopes) in the appropriate module.

Now you can use your custom validation constraint. Simply decorate class property with `@Validate(UserExistsRule)` decorator:

```TypeScript
export class User {
  @IsInt()
  @Validate(UserExistsRule);
  readonly id: number;
}
```

If the user doesn't exist in the database, you should get error with default message "User doesn't exist". Although using `@Validate()` is fine enough, you can write your own decorator, which will be much more convenient. Having written Validator Constraint is quick and easy. We need to just write decorator factory with `registerDecorator()` function.

```TypeScript
export function UserExists(validationOptions?: ValidationOptions) {
  return function (object: any, propertyName: string) {
    registerDecorator({
      name: 'UserExists',
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: UserExistsRule,
    });
  };
}
```
As you can see, you can either write new validator logic, or use written before validator constraint (in our case – `UserExistsRule` class).

Now we can go back to our `User` class, and use `@UserExists` validator instead of `@Validate(UserExistsRule)` decorator.

```TypeScript
export class User {
  @IsInt()
  @UserExists();
  readonly id: number;
}
```

Hope this little article will help you with many common scenarios during your application development with NestJS framework. I use that technique almost every day!