NestJS'te Sıfırdan Bir Zod Validation Pipe Yazmak
NestJS'in class-validator tabanlı ValidationPipe'ı yerine, bu API tüm @Body()/@Query() doğrulamasını tek bir ZodValidationPipe üzerinden yapıyor:
// apps/api/src/common/zod-validation.pipe.ts
@Injectable()
export class ZodValidationPipe implements PipeTransform {
constructor(private readonly schema: ZodType) {}
transform(value: unknown) {
const result = this.schema.safeParse(value);
if (!result.success) {
throw new BadRequestException(result.error.issues);
}
return result.data;
}
}
Kullanımı da tek satır: @Body(new ZodValidationPipe(createPostSchema)) body.
Neden class-validator değil
class-validator, doğrulama kurallarını class alanları üzerinde dekoratörlerle (@IsString(), @MaxLength()) tanımlamanı ister — bu da API'ye özel bir DTO seti anlamına gelir. Ama packages/schemas'daki Zod şemaları zaten web ve admin tarafında da aynen kullanılıyor (form doğrulama, response parse). class-validator'a geçmek bu paylaşımı kırar: DTO'ları class-validator dekoratörleriyle, form doğrulamasını Zod'la iki kere yazmak gerekirdi.
safeParse neden parse değil
schema.parse() hata fırlatır ve NestJS'in kendi exception filtresine düşer — hata mesajı formatı Zod'un iç yapısına bağlı kalır. safeParse() ile {success, data}/{success, error} şeklinde bir sonuç alıp, hatayı kendi BadRequestException'ımıza result.error.issues olarak taşıyoruz — client'a giden 400 response'un şekli üzerinde kontrol bizde kalıyor, framework'ün varsayılan davranışına bağımlı değiliz.
Tek pipe, her endpoint
PipeTransform arayüzünü bir kere implemente ettikten sonra, her yeni endpoint için tek ihtiyacımız olan şey doğru şema — createProjectSchema, updatePostSchema, listProjectsQuerySchema, hepsi aynı pipe'ı, farklı şemayla çağırıyor. Yeni bir kaynak (resource) eklemek yeni bir pipe yazmayı gerektirmiyor.