Search code examples
nestjsdto

How can I validate an array of enum values with Nestjs


I feel like a combination of this thread and this thread is what I need to implement, I'm having trouble drawing them together.

I have a DTO that contains an enum.

Using Postman, I am sending a PurchasableType of FOO and expecting to get an error of some sort. Reading through the above links, it seems like the process is quite involved; which makes me thing I'm completely missing the point.

How can I use the validation pipe(s) to make sure only the values in the purchasable-type.enum.ts are allowed?

Thank you for any suggestions!

// create-order.dto.ts

import { IsEmail, IsNotEmpty, IsEnum } from 'class-validator';
import { PurchasableType } from '../enum/purchaseable-type.enum';

export class CreateOrderDto {
  @IsNotEmpty()
  readonly userId: string;

  @IsNotEmpty()
  readonly locationId: string;

  @IsNotEmpty()
  @IsEnum(PurchasableType)
  readonly purchasableType: PurchasableType;

  @IsNotEmpty()
  @IsEmail()
  readonly email: string;
}
// purchasable-type.enum.ts

export enum PurchasableType {
  CLINIC = 'CLINIC',
  EVENT = 'EVENT',
  LESSON = 'LESSON',
  RESERVATION = 'RESERVATION',
  TEAM = 'TEAM',
}

EDIT

It seems I was also not defining the entity correctly, and that may have been the main issue. I am still curious if my implementation good/bad.

// order.entity.ts

...
import { PurchasableType } from '../enum/purchaseable-type.enum';

@Entity()
export class Order extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

@Column({
    type: 'enum',
    enum: PurchasableType,
  })

Now when I send a purchasableType of foo I am getting a 500 error. If I send any valid value that is within the enum I am getting a 200/201.

EDIT 2

Sure - here is a bit wider view of what I've got. Everything seems to be working properly, I'd just like to have a better grasp of what was really happening.

// event.controller.ts

@Post('/:id/orders')
  async purchaseEventTickets(@Body() createOrderDto: CreateOrderDto): 
    Promise<Order> {
    return await this.eventService.purchaseEventTickets(createOrderDto);
  }

// create-order.dto.ts

export class CreateOrderDto {
    @IsNotEmpty()
    @IsEnum(PurchasableType)
    readonly purchasableType: PurchasableType;
}
// event.service.ts

async purchaseEventTickets(createOrderDto: CreateOrderDto): Promise<Order> {
    ...
    return await this.orderRepository.createOrder(createOrderDto);
}

// order.repository.ts

async createOrder(createOrderDto: CreateOrderDto): Promise<Order> {
    const { purchasableType } = createOrderDto;

    const order = this.create();

    order.purchasableType = purchasableType;

    try {
        await order.save();
    } catch (error) {
        this.logger.error(`Failed to create the order: ${error.stack}`);

        throw new InternalServerErrorException();
    }

    return order;
}

Using Postman, if I send an invalid value of "Foo" as a PurchasableType I get the expected error.


Solution

  • Here is what your create-dto looks like that contains an enum.

    // create-order.dto.ts
    
    import { IsEmail, IsNotEmpty, IsEnum } from 'class-validator';
    import { PurchasableType } from '../enum/purchaseable-type.enum';
    
    export class CreateOrderDto {
    
        ...
    
        @IsNotEmpty()
        @IsEnum(PurchasableType)
        readonly purchasableType: PurchasableType;
    }
    
    

    Here is what that enum file looks like:

    // purchasable-type.enum.ts
    
    export enum PurchasableType {
      CLINIC = 'CLINIC',
      EVENT = 'EVENT',
      LESSON = 'LESSON',
      RESERVATION = 'RESERVATION',
      TEAM = 'TEAM',
    }
    
    

    From there I can confidently expect the value of the enum to be one of the above values. If some other value comes through, nest will throw a validation error.

    Additionally, If you are attempting to use a nested object (or something with multiple attributes or an array) you can do something like this in your DTO:

    import { PurchasableType } from '../interface/purchasable-type.interface';
    ...
    
    @ApiProperty()
    @IsArray()
    @ArrayMinSize(7)
    @ArrayMaxSize(7)
    @ValidateNested({ each: true })
    @Type(() => PurchasableType)
    @IsNotEmpty()
    readonly PurchasableType: PurchasableType[];
    
    ...