The time has come for a real test, legionary! In this project, you will combine all the validation techniques learned in this module to build a complete gate protection system for the Roman Empire.
You will build a legion management system with full input data validation. The system consists of DTOs for creating legions, legionaries, and weapons, with custom validators and transformations.
1// enums.ts
2export enum LegionaryRank {
3 MILES = 'miles',
4 OPTIO = 'optio',
5 CENTURIO = 'centurio',
6 TRIBUNUS = 'tribunus',
7 LEGATUS = 'legatus',
8}
9
10export enum WeaponType {
11 GLADIUS = 'gladius',
12 PILUM = 'pilum',
13 SCUTUM = 'scutum',
14 PUGIO = 'pugio',
15}
16
17export enum LegionStatus {
18 ACTIVE = 'active',
19 RESERVES = 'reserves',
20 DISBANDED = 'disbanded',
21}1// weapon.dto.ts
2import { IsString, IsNotEmpty, IsEnum, IsNumber, Min, Max } from 'class-validator';
3
4export class CreateWeaponDto {
5 @IsString()
6 @IsNotEmpty()
7 name: string;
8
9 @IsEnum(WeaponType)
10 type: WeaponType;
11
12 @IsNumber()
13 @Min(1)
14 @Max(100)
15 damage: number;
16
17 @IsNumber()
18 @Min(0)
19 @Max(10)
20 weight: number;
21}1// legionary.dto.ts
2import {
3 IsString, IsNotEmpty, IsEnum, IsNumber,
4 Min, Max, MinLength, MaxLength, IsArray,
5 ArrayMinSize, ValidateNested, IsOptional, Matches,
6} from 'class-validator';
7import { Type, Transform, Exclude } from 'class-transformer';
8
9export class CreateLegionaryDto {
10 @IsString()
11 @IsNotEmpty()
12 @MinLength(2)
13 @MaxLength(50)
14 @Transform(({ value }) => value.trim())
15 name: string;
16
17 @IsEnum(LegionaryRank)
18 rank: LegionaryRank;
19
20 @IsNumber()
21 @Min(16)
22 @Max(65)
23 age: number;
24
25 @Matches(/^LEG-[A-Z]{2,4}-\d{4}$/, {
26 message: 'ID must have the format LEG-XX-0000',
27 })
28 militaryId: string;
29
30 @IsArray()
31 @ArrayMinSize(1)
32 @IsString({ each: true })
33 skills: string[];
34
35 @ValidateNested()
36 @Type(() => CreateWeaponDto)
37 primaryWeapon: CreateWeaponDto;
38
39 @IsOptional()
40 @ValidateNested()
41 @Type(() => CreateWeaponDto)
42 secondaryWeapon?: CreateWeaponDto;
43}1// legion.dto.ts
2export class CreateLegionDto {
3 @IsString()
4 @IsNotEmpty()
5 @MinLength(3)
6 name: string;
7
8 @IsEnum(LegionStatus)
9 status: LegionStatus;
10
11 @IsNumber()
12 @Min(1000)
13 @Max(6000)
14 maxSoldiers: number;
15
16 @ValidateNested({ each: true })
17 @Type(() => CreateLegionaryDto)
18 @IsArray()
19 @ArrayMinSize(1)
20 soldiers: CreateLegionaryDto[];
21}1// validators/is-valid-military-id.ts
2import {
3 ValidatorConstraint,
4 ValidatorConstraintInterface,
5 registerDecorator,
6 ValidationOptions,
7} from 'class-validator';
8
9@ValidatorConstraint({ async: false })
10class IsValidMilitaryIdConstraint implements ValidatorConstraintInterface {
11 validate(value: string): boolean {
12 if (!value) return false;
13 const parts = value.split('-');
14 if (parts.length !== 3) return false;
15 if (parts[0] !== 'LEG') return false;
16 const numericPart = parseInt(parts[2], 10);
17 return numericPart > 0 && numericPart <= 9999;
18 }
19
20 defaultMessage(): string {
21 return 'Invalid military identifier';
22 }
23}
24
25export function IsValidMilitaryId(options?: ValidationOptions) {
26 return function (object: object, propertyName: string) {
27 registerDecorator({
28 target: object.constructor,
29 propertyName,
30 options,
31 validator: IsValidMilitaryIdConstraint,
32 });
33 };
34}1// legionary-response.dto.ts
2import { Exclude, Expose, Transform } from 'class-transformer';
3
4export class LegionaryResponseDto {
5 @Expose()
6 name: string;
7
8 @Expose()
9 rank: string;
10
11 @Expose()
12 @Transform(({ value }) => value > 10 ? 'Veteranus' : 'Tiro')
13 experienceLevel: string;
14
15 @Exclude()
16 secretMissionCode: string;
17
18 @Exclude()
19 salary: number;
20}1// main.ts
2import { NestFactory } from '@nestjs/core';
3import { ValidationPipe } from '@nestjs/common';
4
5async function bootstrap() {
6 const app = await NestFactory.create(AppModule);
7 app.useGlobalPipes(new ValidationPipe({
8 whitelist: true,
9 forbidNonWhitelisted: true,
10 transform: true,
11 transformOptions: {
12 enableImplicitConversion: true,
13 },
14 }));
15 await app.listen(3000);
16}
17bootstrap();That was a complete overview of the validation system in NestJS! Your gates are now protected by the finest guards in the entire Empire. Congratulations, legionary!