파이프는 라우팅 핸들러가 실행되기 전에 개입하여, 데이터를 변환할 수 있다.
파이프는 크게 변환, 유효성 검증의 역할을 한다.
파이프를 사용하려면 파이프 클래스의 인스턴스나 클래스를, 사용하려는 컨텍스트에 바인드해야한다.
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
위 예시처럼 메스드 수준으로 등록이 가능하다. 핸들러가 실행되는 경우, id 매개변수는 반드시 숫자형이라는 것을 보증한다. 만약 GET localhost:3000/abc와 에서 abc와 같이, 숫자로 변환될 수 없는 매개변수가 들어오는 경우, 파이프 단에서 예외가 발생하여 다음 응답을 보내게 된다.
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
@Get()
async findOne(@Query('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
추가적으로 @Param(), @Query() 외에도 @Body()을 통해 요청 페이로드 수준으로 등록이 가능하다.
라우팅 핸들러 수준으로 등록
@UsePipes() 데코레이터로 파이프를 라우팅 핸들러 자체에 등록하면, 해당 핸들러의 모든 매개변수에 대해 작동합니다.
컨트롤러 수준으로 등록
@UsePipes() 데코레이터로 컨트롤러에 등록하면, 해당 컨트롤러의 모든 핸들러의 모든 매개변수에 대해 작동합니다.
전역으로 등록
ValidationPipe같은 경우 최대한 범용적으로 쓸 수 있게 만들어진 덕분에 애플리케이션의 모든 라우팅 핸들러에 적용할 수 있습니다.
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
@Get()
async findAll(
@Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
@Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
return this.catsService.findAll({ activeOnly, page });
}
먼저 아래의 예시를 통해 받은 데이터를 그대로 보내주는 커스텀 파이프를 통해 간단해 구현해보자.
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class PassthroughPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
return value;
}
}
다음으로 ParseIntPipe를 만들어보자.
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ObjectSchema } from 'joi';
@Injectable()
export class JoiValidationPipe implements PipeTransform {
constructor(private schema: ObjectSchema) {}
transform(value: any, metadata: ArgumentMetadata) {
const { error } = this.schema.validate(value);
if (error) {
throw new BadRequestException('Validation failed');
}
return value;
}
}
위 예시는 joi를 사용하는 검증이고, 파이프의 등록은 아래와 같다.
@Post()
@UsePipes(new JoiValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
참고: 네스트 공식문서