~/zafer
Tüm yazılara dön
2 dk okuma

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.