Implement PipeTransform with typed generics, validate the value using Types.ObjectId.isValid(), throw BadRequestException if invalid, and return a Types.ObjectId instance if valid. The pipe both validates and transforms — the handler receives a strongly typed ObjectId, not a raw string.
PipeTransform<Input, Output> generics make the transformation contract explicit.
Always throw BadRequestException (400) for invalid input — not NotFoundException.
Return the transformed type — handlers should receive the final type, not raw strings.
Pipes are @Injectable() — they can inject services for async database-backed validation.
For async validation, return Promise<Output> from transform().