Careeroza
What is a Pipe
Transform and Validate
A pipe receives one handler argument (body, param or query) before the handler runs. It either returns a (possibly converted) value or throws an exception, usually BadRequestException.
- Transformation:
"7"becomes7,"true"becomestrue. - Validation: reject bad input with a 400 before your code sees it.
@Controller('products')
export class ProductsController {
// pipe class: NestJS creates (and reuses) the instance
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {}
// pipe instance with options
@Get(':id/stock')
stock(@Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE })) id: number) {}
}Pipes are Chained
Example
Pipes for one argument run left to right, each receiving the previous output. Here is the same idea with plain functions, mimicking @Query('page', new DefaultValuePipe(1), ParseIntPipe):
class BadRequestException extends Error {}
type Pipe = (value: unknown) => unknown;
const defaultValue = (def: unknown): Pipe => (v) => (v === undefined || v === '' ? def : v);
const parseInt: Pipe = (v) => {
if (typeof v === 'number') return v;
if (typeof v !== 'string' || !/^-?\d+$/.test(v)) throw new BadRequestException('Validation failed (numeric string is expected)');
return Number(v);
};
const min = (n: number): Pipe => (v) => {
if ((v as number) < n) throw new BadRequestException('page must not be less than ' + n);
return v;
};
function applyPipes(value: unknown, pipes: Pipe[]): unknown {
return pipes.reduce((acc, pipe) => pipe(acc), value);
}
for (const raw of [undefined, '3', 'abc', '-2']) {
try {
const page = applyPipes(raw, [defaultValue(1), parseInt, min(1)]);
console.log(JSON.stringify(raw), '->', page, typeof page);
} catch (e) {
console.log(JSON.stringify(raw), '-> 400', (e as Error).message);
}
}Output
undefined -> 1 number "3" -> 3 number "abc" -> 400 Validation failed (numeric string is expected) "-2" -> 400 page must not be less than 1