chanlito / nestjs-extensions

[WIP] A bunch of useful and opinionated filters, modules, pipes... to use with Nest framework. 😻
42 stars 0 forks source link

Could DtoPipe return entity, and could it use params/queryParams instead of @Body? #5

Closed chriszrc closed 6 years ago

chriszrc commented 6 years ago

It's nice that nest will convert post bodies to an entity/dto, but I want the same functionality for coercing params into an object as well. In Spring, this is handled by @ModelAttribute.

Could the DtoPipe be modified to do this?

chanlito commented 6 years ago

Yes should work just fine.

chanlito commented 6 years ago

For example:

// playground.dto.ts

import { Transform } from 'class-transformer';
import {
  IsInt,
  IsNotEmpty,
  IsOptional,
  IsString,
  Max,
  Min
} from 'class-validator';
import { Dto } from 'nestjs-extensions';

@Dto()
export class PostDto {
  @IsNotEmpty()
  @IsString()
  title!: string;

  @IsString()
  @IsOptional()
  description?: string;

  @IsNotEmpty()
  @Transform(x => +x)
  count!: number;
}

@Dto()
export class QueryDto {
  @Min(1)
  @Max(20)
  @Transform(x => +x)
  limit!: number;

  @Min(0)
  @Transform(x => +x)
  offset!: number;
}

@Dto()
export class ParamsDto {
  @IsInt()
  @Transform(x => +x)
  id!: number;

  @IsInt()
  @Transform(x => +x)
  postId!: number;
}
// playground.controller.ts

import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';

import { ParamsDto, PostDto, QueryDto } from './playground.dto';

@Controller('playground')
export class PlayGroundController {
  @Post()
  async post(@Body() { title, description, count }: PostDto) {
    return { title, description, count };
  }

  @Get()
  async get(@Query() { limit, offset }: QueryDto) {
    return {
      limit: { value: limit, type: typeof limit },
      offset: { value: offset, type: typeof offset }
    };
  }

  @Get(':id/posts/:postId')
  async getOne(@Param() { id, postId }: ParamsDto) {
    return {
      id: { value: id, type: typeof id },
      postId: { value: postId, type: typeof postId }
    };
  }
}
chriszrc commented 6 years ago

It works thanks! And it appears it's even faster than what I was doing without it, nice!

I actually want the whole dto to pass to a service, so I'm using it like this:

@Get('/:x/:y/:z')
  async findByTileDto(@Param() createTileDto: CreateTileDto, @Res() res:Response) {...}

However, even though my entity looks like this:

@Dto()
export class CreateTileDto {
  @IsNotEmpty()
  @IsInt()
  x: number;

  @IsNotEmpty()
  @IsInt()
  y: number;

  @IsNotEmpty()
  @IsInt()
  z: number;
}

I get back a 422, with a message that my values aren't numbers. And of course, it's right, they're strings, since they're coming from params. Is there a way to use the ParseIntPipe here, or am I missing something else?

chanlito commented 6 years ago

@Transform(x => +x) add this on your property, internally it's uses class-transformer before doing validation so this would make it to a number be it validates.