Exclude/Expose decorator for properties in class inheritance
See original GitHub issueI have 2 projects sharing some entities, and I’m trying to find a way to keep the validation consistent through all my application, and if possible, reuse part of the code instead of copying/pasting it.
My example
Classes needed
// This class describes the database table
class UserDB {
id: string; // generated by DB when a new record is added
email: string; // NOT NULL
age?: number;
firstName?: string;
lastName?: string;
}
// This class describes all fields validations
class User {
@IsUUID("4") @JSONSchema({ description: `User's ID` })
id: string;
@IsEmail() @JSONSchema({ description: `User's email` })
email: string;
@IsOption() @IsInt() @IsPositive() @Min(18) @JSONSchema({ description: `User's age` })
age?: number;
@IsOption() @MinLength(3) @JSONSchema({ description: `User's first name` })
firstName?: string;
@IsOption() @MinLength(5) @JSONSchema({ description: `User's last name` })
lastName?: string;
}
Create User API schemas
class CreateUserRequest {
@IsEmail() @JSONSchema({ description: `User's email` })
email: string;
@IsOption() @IsInt() @IsPositive() @Min(18) @JSONSchema({ description: `User's age` })
age?: number;
@IsOption() @MinLength(3) @JSONSchema({ description: `User's first name` })
firstName?: string;
@IsOption() @MinLength(5) @JSONSchema({ description: `User's last name` })
lastName?: string;
}
class CreateUserResponse extends User { }
Change Email API schemas
class ChangeEmailRequest {
@IsEmail() @JSONSchema({ description: `New email address` })
email: string;
}
class ChangeEmailResponse extends User { }
As we can see, we keep copying/pasting all the validations and descriptions from class to class, so I’m trying to find a better way to reuse the code so that it is also easier to maintain.
Solution 1
I create a common class containing the “base” properties.
class UserCommon {
@IsEmail() @JSONSchema({ description: `User's email` })
email: string;
@IsOption() @IsInt() @IsPositive() @Min(18) @JSONSchema({ description: `User's age` })
age?: number;
@IsOption() @MinLength(3) @JSONSchema({ description: `User's first name` })
firstName?: string;
@IsOption() @MinLength(5) @JSONSchema({ description: `User's last name` })
lastName?: string;
}
class User extends UserCommon {
@IsUUID("4") @JSONSchema({ description: `User's ID` })
id: string;
}
And then try to reuse the “base” class whenever possible
class CreateUserRequest extends UserCommon {}
class CreateUserResponse extends User {}
class ChangeEmailRequest {
@IsEmail() @JSONSchema({ description: `User's email` })
email: string;
}
class ChangeEmailResponse extends User {}
Solution 2
Create a base class describing all the fields with their validations.
class User {
@IsUUID("4") @JSONSchema({ description: `User's ID` })
id: string;
@IsEmail() @JSONSchema({ description: `User's email` })
email: string;
@IsOption() @IsInt() @IsPositive() @Min(18) @JSONSchema({ description: `User's age` })
age?: number;
@IsOption() @MinLength(3) @JSONSchema({ description: `User's first name` })
firstName?: string;
@IsOption() @MinLength(5) @JSONSchema({ description: `User's last name` })
lastName?: string;
}
And then extend it excluding or exposing fields
class CreateUserRequest extends User {
@Exclude()
id: string;
}
class CreateUserResponse extends User {}
class ChangeEmailRequest {
@Expose()
email: string;
}
class ChangeEmailResponse extends User {}
Solution 1
can be already implemented, even tho it will be hard to isolate the “common” properties when the app starts becoming big. i.e. if I introduce an UpdateUser API, probably I want to keep the email out of it, so I have to remove the email from the UserCommon
class.
Solution 2
would be really flexible but I guess it is not supported currently by this library, right? Any chance to get this implemented?
Do you have any feedback? or any smarter way to achieve this result?
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:7 (4 by maintainers)
Top GitHub Comments
@epiphone few feedback:
class-validator-jsonschema
so the swagger file will not be effectedPick
will require the declaration of each propertyI’ve come out with a new solution much lighter.
Now we can define all the validations on the
User
class, and create a new class using the validation already defined, specifying the list of properties to inherit (with their validations settings).Doesn’t it work without
keyOfStringsOnly: true
if we extendstrEnum
s type to something likefunction strEnum<T extends string | symbol | number>(o: T[]): { [P in T]: P }
?