We use cookies to enhance your experience on the site
CodeWorlds

Main Project — Complete Imperial Validation System

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.

Task

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.

Step 1 — Enums and Base Types

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}

Step 2 — Weapon DTO with Validation

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}

Step 3 — Legionary DTO with Nested Validation

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}

Step 4 — Legion DTO with Array of Nested Objects

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}

Step 5 — Custom Validator

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}

Step 6 — Response DTO with Transformations

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}

Step 7 — ValidationPipe Configuration

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!

Go to CodeWorlds