Skip to Content
Validationclass-validator

Validation with class-validator

Since Vovk.ts was heavily inspired by NestJS , it also supports the class-validator  library for both server-side and client-side validation.

npm install vovk-dto@draft
src/modules/user/UserController.ts
import { prefix, post, operation } from 'vovk'; import { withDto } from 'vovk-dto'; import { UpdateUserBodyDto, UpdateUserParamsDto, UpdateUserQueryDto, UpdateUserResponseDto } from './UserDto'; @prefix('users') export default class UserController { @operation({ summary: 'Update user', description: 'Update user by ID with Dto validation', }) @post('{id}') static updateUser = withDto({ body: UpdateUserBodyDto, params: UpdateUserParamsDto, query: UpdateUserQueryDto, output: UpdateUserResponseDto, async handle(req) { const body = await req.vovk.body(); const query = req.vovk.query(); const params = req.vovk.params(); console.log(body instanceof UpdateUserBodyDto); // true console.log(query instanceof UpdateUserQueryDto); // true console.log(params instanceof UpdateUserParamsDto); // true }, }); }

As shown above, DTOs use the @JSONSchema decorator to describe properties when converted to JSON Schema. vovk-dto  relies on targetConstructorToSchema from class-validator-jsonschema  to produce schemas for the OpenAPI spec, codegen, and function calling.

Also note that req.vovk exposes validated input. The req.vovk.body(), req.vovk.query(), and req.vovk.params() methods return instances of the corresponding DTO classes. You can still access raw data via req.json(), req.nextUrl.searchParams, and the second argument of the handle function.

Client-Side Validation with DTOs

Because class-validator-jsonschema  converts DTOs to JSON Schema, the standard client-side flow with vovk-ajv can be limited by the library’s known constraints .

For the most robust approach, use the vovk-dto/validateOnClient module. It exports a validateOnClient function you can reference in the vovk.config import:

vovk.config.mjs
/** @type {import('vovk').VovkConfig} */ const config = { outputConfig: { imports: { validateOnClient: 'vovk-dto/validateOnClient', }, }, }; export default config;

Client-side validation requires transforming input data into the corresponding DTO class using class-transformer . If the input is a plain object or FormData, it will not be validated.

import { UserRPC } from 'vovk-client'; import { plainToInstance } from 'class-transformer'; import { UpdateUserBodyDto, UpdateUserResponseDto } from '@/modules/user/UserDto'; const respData = await UserRPC.updateUser({ body: plainToInstance(UpdateUserBodyDto, { name: 'John Doe', age: 42, } satisfies UpdateUserBodyDto), // ... same for query and params // optionally transform response data to a DTO transform: (data) => plainToInstance(UpdateUserResponseDto, data) });

In rare cases where the data is neither a DTO instance, a plain object, nor FormData, you can skip validation with disableClientValidation.

import { UserRPC } from 'vovk-client'; await UserRPC.updateUser({ body: nonDtoInstance, // not a DTO instance, plain object, or FormData disableClientValidation: true, });

dto-mapped-types

To apply pick, omit, partial, and intersection patterns to DTOs, use dto-mapped-types —a fork of @nestjs/mapped-types  without extra dependencies. It provides the same features as the original package without requiring @nestjs/common.

src/modules/user/UserDto.ts
import { IsString, IsEmail, MinLength, MaxLength, IsUUID } from 'class-validator'; import { OmitType } from 'dto-mapped-types'; @JSONSchema({ description: 'User DTO', }) export class UserDto { @IsUUID(4, { message: 'Invalid uuid format' }) id: string; @IsString({ message: 'Name must be a string' }) @MinLength(2, { message: 'Name must be at least 2 characters' }) @MaxLength(20, { message: 'Name must be at most 20 characters' }) name: string; @IsEmail({}, { message: 'Invalid email format' }) email: string; } @JSONSchema({ description: 'Create User DTO', }) export class CreateUserDto extends OmitType(UserDto, ['id']) { @IsString({ message: 'Password must be a string' }) password: string; }

FormData

To emit a proper JSON Schema for files when using isForm: true in the validation handler, provide an OAS-compatible schema for file fields via @JSONSchema:

import { JSONSchema } from 'class-validator-jsonschema'; export class UploadFileDto { @JSONSchema({ type: 'string', format: 'binary', description: 'File to upload', }) file!: File; }
Last updated on