Careeroza
Custom Pipes
Implementing PipeTransform
A custom pipe is an @Injectable() class with a transform(value, metadata) method. ShopEase uses URL slugs like /products/slug/boat-airdopes-141; this pipe normalises and checks them.
src/common/pipes/slug.pipe.ts
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class SlugPipe implements PipeTransform<string, string> {
transform(value: string, metadata: ArgumentMetadata): string {
const slug = value.trim().toLowerCase();
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
throw new BadRequestException(`${metadata.data} must be a valid slug (a-z, 0-9 and hyphens)`);
}
return slug;
}
}
// usage: @Get('slug/:slug') bySlug(@Param('slug', SlugPipe) slug: string) {}A Pipe that Loads an Entity
A pipe can inject services. This one turns an id into the actual product, or a 404:
src/products/pipes/product-by-id.pipe.ts
import { Injectable, NotFoundException, PipeTransform } from '@nestjs/common';
import { ProductsService } from '../products.service';
import { Product } from '../entities/product.entity';
@Injectable()
export class ProductByIdPipe implements PipeTransform<string, Promise<Product>> {
constructor(private readonly products: ProductsService) {}
async transform(value: string): Promise<Product> {
const product = await this.products.findById(Number(value));
if (!product) throw new NotFoundException(`Product ${value} not found`);
return product;
}
}
// usage: @Get(':id') findOne(@Param('id', ProductByIdPipe) product: Product) { return product; }Exercise: Write a TrimPipe that trims every string property of a body object (one level deep) and leaves other values unchanged.
Show answer
import { Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class TrimPipe implements PipeTransform {
transform(value: unknown) {
if (value === null || typeof value !== 'object' || Array.isArray(value)) return value;
return Object.fromEntries(
Object.entries(value).map(([k, v]) => [k, typeof v === 'string' ? v.trim() : v]),
);
}
}