class-validator-jsonschema icon indicating copy to clipboard operation
class-validator-jsonschema copied to clipboard

How to make a DTO created with a custom mapping library appear at the validationMetadatasToSchemas return object?

Open finom opened this issue 10 months ago • 1 comments

Thank you for the great project. I'm working on a library that supports DTOs for data validation. The validation should work on Edge Runtime, so I had to create a fork of @nestjs/mapped-types with some basic modifications to avoid import errors.

class-validator-jsonschema works perfect with regular DTOs, but struggles to identify a mapped DTO.

import { IsString, IsEmail, MinLength, MaxLength, IsUUID } from 'class-validator';
import { OmitType } from 'vovk-mapped-types';
import { JSONSchema, validationMetadatasToSchemas } from 'class-validator-jsonschema';

@JSONSchema({
  description: 'User DTO',
})
export default 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 cannot exceed 20 characters' })
  name: string;

  @IsString({ message: 'Email must be a string' })
  @IsEmail({}, { message: 'Invalid email format' })
  email: string;
}

@JSONSchema({
  description: 'Create user DTO',
})

export class CreateUserDto extends OmitType(UserDto, ['id'] as const) {}

console.log('validationMetadatasToSchemas', validationMetadatasToSchemas())

The code logs

{
  UserDto: {
    properties: { id: [Object], name: [Object], email: [Object] },
    type: 'object',
    required: [ 'id', 'name', 'email' ]
  },
  OmitClassType: {
    properties: { name: [Object], email: [Object] },
    type: 'object',
    required: [ 'name', 'email' ]
  },
}

Where OmitClassType is an internal class of the mapping library that can be ignored.

As you can see CreateUserDto does not appear at the output, but if I add at least one extra property:

export class CreateUserDto extends OmitType(UserDto, ['id'] as const) {
  @IsEmail({}, { message: 'secondary email' })
  email2: string;
}

It works as expected:

 {
  UserDto: {
    properties: { id: [Object], name: [Object], email: [Object] },
    type: 'object',
    required: [ 'id', 'name', 'email' ],
    description: 'User DTO'
  },
  OmitClassType: {
    properties: { name: [Object], email: [Object] },
    type: 'object',
    required: [ 'name', 'email' ]
  },
  CreateUserDto: {
    properties: { email2: [Object], name: [Object], email: [Object] },
    type: 'object',
    required: [ 'email2', 'name', 'email' ],
    description: 'Create user DTO'
  }
}

Is there a way to avoid using class-validator decorators in order to make it appear at the returned object? If not, why not to also use JSONSchema to mark the class as a DTO (if it makes sense)?

Thank you for your help.

finom avatar Feb 12 '25 22:02 finom

I solved my issue by using targetConstructorToSchema

import { targetConstructorToSchema } from 'class-validator-jsonschema';

const schema = targetConstructorToSchema(dto);

Not sure if I should close the issue since the main question with validationMetadatasToSchemas is unsolved. Let me know what you think.

finom avatar Feb 13 '25 09:02 finom