Skip to content

Hata Yönetimi ​

@yildizpay/http-adapter, her ham hatayı — HTTP başarısızlıkları, OS seviyesindeki ağ hataları ve beklenmedik exception'lar — yapılandırılmış, tipli bir exception sınıfına dönüştürür. catch bloklarınızın ham durum kodlarını veya hata kodlarını manuel olarak incelemesine gerek kalmaz.

Exception Hiyerarşisi ​

BaseAdapterException
├── HttpException                    (herhangi bir HTTP yanıt hatası)
│   ├── BadRequestException          (400)
│   ├── UnauthorizedException        (401)
│   ├── ForbiddenException           (403)
│   ├── NotFoundException            (404)
│   ├── ConflictException            (409)
│   ├── UnprocessableEntityException (422)
│   ├── TooManyRequestsException     (429)  ← isRetryable() = true
│   ├── InternalServerErrorException (500)
│   ├── BadGatewayException          (502)  ← isRetryable() = true
│   ├── ServiceUnavailableException  (503)  ← isRetryable() = true
│   ├── GatewayTimeoutException      (504)  ← isRetryable() = true
│   └── ... (tüm 4xx / 5xx kodları)
├── NetworkException                 (OS seviyesinde bağlantı hataları)
│   ├── ConnectionRefusedException   (ECONNREFUSED)  ← isRetryable() = true
│   ├── TimeoutException             (ETIMEDOUT / ECONNABORTED / AbortError)  ← isRetryable() = true
│   ├── SocketResetException         (ECONNRESET)  ← isRetryable() = true
│   ├── DnsResolutionException       (ENOTFOUND / EAI_AGAIN)
│   └── HostUnreachableException     (EHOSTUNREACH / ENETUNREACH)
├── ValidationException              (yanıt bir ResponseValidator'dan geçemedi)
├── UnknownException                 (sınıflandırılamayan herhangi bir hata)
└── CircuitBreakerOpenException      (devre açık, istek gönderilmedi)

Exception'ları Türüne Göre Yakalama ​

typescript
import {
  NotFoundException,
  TooManyRequestsException,
  TimeoutException,
  ConnectionRefusedException,
  CircuitBreakerOpenException,
  UnknownException,
} from '@yildizpay/http-adapter';

try {
  const response = await adapter.send<OdemeYaniti>(request);
} catch (error) {
  if (error instanceof NotFoundException) {
    // HTTP 404
    console.error('Kaynak bulunamadı:', error.response.data);

  } else if (error instanceof TooManyRequestsException) {
    // HTTP 429 — Retry-After başlık değerini kullan
    const retryAfterMs = error.getRetryAfterMs();
    console.warn(`İstek limiti aşıldı. ${retryAfterMs}ms sonra tekrar deneyin`);

  } else if (error instanceof TimeoutException) {
    // ETIMEDOUT / AbortError — downstream servis çok yavaş
    console.error('İstek zaman aşımına uğradı:', error.code);

  } else if (error instanceof ConnectionRefusedException) {
    // ECONNREFUSED — downstream servis çalışmıyor
    console.error('Servis çalışmıyor:', error.requestContext?.url);

  } else if (error instanceof CircuitBreakerOpenException) {
    // Devre açık — sunucuya istek atmadan hızlı başarısız ol
    console.error('Circuit breaker açık. İstek gönderilmiyor.');

  } else if (error instanceof UnknownException) {
    console.error('İşlenmeyen hata:', error.toJSON());
  }
}

Tip Koruyucular (Type Guards) ​

instanceof olmadan daralma tercih ediyorsanız — fonksiyonel pipeline'larda veya modül sınırlarını geçerken kullanışlıdır — her exception sınıfının bir tip koruyucusu mevcuttur:

typescript
import {
  isHttpException,
  isTimeoutException,
  isNetworkException,
  isCircuitBreakerOpenException,
} from '@yildizpay/http-adapter';

function handleError(error: unknown): void {
  if (isTimeoutException(error)) {
    // TypeScript artık biliyor: error, TimeoutException türünde
    retryIsleminiPlanla(error.requestContext?.url);
  } else if (isHttpException(error)) {
    // TypeScript artık biliyor: error, HttpException türünde
    izlemeRaporla(error.response.status, error.response.data);
  }
}

isRetryable() Sinyali ​

Her exception, isRetryable(): boolean metodunu sunar. Özel retry decorator'ları uygularken veya uygulama katmanında bir hatayı iletip iletmeme kararı verirken kullanışlıdır:

typescript
} catch (error) {
  if (error instanceof BaseAdapterException && error.isRetryable()) {
    return islemiYenidenDene();
  }
  throw error;
}

Yeniden denenebilir exception'lar: TooManyRequestsException (429), BadGatewayException (502), ServiceUnavailableException (503), GatewayTimeoutException (504), TimeoutException, SocketResetException, ConnectionRefusedException.

toJSON() ile Yapılandırılmış Loglama ​

Tüm exception'lar toJSON()'ı geçersiz kılar; bu sayede yapılandırılmış logger'larla (Pino, Winston vb.) uyumlu hale gelirler. JSON.stringify(error) düz bir Error'dan elde edeceğiniz boş {} yerine tam, iç içe bir nesne üretir:

typescript
} catch (error) {
  if (error instanceof BaseAdapterException) {
    logger.error(error.toJSON());
    // {
    //   name: 'NotFoundException',
    //   message: 'Not Found',
    //   code: 'ERR_NOT_FOUND',
    //   stack: '...',
    //   response: {
    //     status: 404,
    //     data: { detail: 'Ödeme kaydı bulunamadı' },
    //     request: {
    //       method: 'GET',
    //       url: 'https://api.example.com/payments/123',
    //       correlationId: 'corr-abc'
    //     }
    //   }
    // }
  }
}

RequestContext — Güvenli İstek Meta Verisi ​

Her exception, kaynak istekten alınan method, url ve correlationId içeren bir RequestContext nesnesi taşır. Başlıklar ve gövde, loglarda auth token veya KKB sızıntısını önlemek için kasıtlı olarak hariç tutulmuştur.

typescript
} catch (error) {
  if (error instanceof NetworkException) {
    logger.warn({
      event: 'network_failure',
      exception: error.name,
      request: error.requestContext, // { method, url, correlationId }
    });
  }
}

Yanıt Doğrulayıcılar ​

HTTP çağrısı başarılı olduktan sonra, yanıt kodunuza ulaşmadan önce şema kısıtlamalarını veya iş kurallarını otomatik olarak uygulamak için isteklere doğrulayıcılar ekleyin. Doğrulayıcılar sırayla çalışır; ilk exception fırlatan doğrulayıcı zinciri durdurur.

typescript
import { ResponseValidator, ValidationException, Response } from '@yildizpay/http-adapter';

class OdemeDurumValidator implements ResponseValidator<IyzicoYaniti> {
  validate(response: Response<IyzicoYaniti>): void {
    if (response.data.status !== 'success') {
      throw new ValidationException(
        `Ödeme başarısız: ${response.data.errorMessage}`,
        response,
      );
    }
  }
}

// Herhangi bir şema doğrulama kütüphanesiyle çalışır — Zod, Joi vb.'ye sıfır bağımlılık
class OdemeSchemaValidator implements ResponseValidator<unknown> {
  validate(response: Response<unknown>): void {
    IyzicoYanitiSchema.parse(response.data); // Zod uyumsuzlukta exception fırlatır
  }
}

const request = new RequestBuilder('https://api.iyzipay.com')
  .setEndpoint('/payment/auth')
  .setMethod(HttpMethod.POST)
  .setBody(dto)
  .validateWith(new OdemeSchemaValidator(), new OdemeDurumValidator())
  .build();

Doğrulama başarısızlığını yakalamak:

typescript
import { isValidationException } from '@yildizpay/http-adapter';

} catch (error) {
  if (isValidationException(error)) {
    console.error('Doğrulama başarısız:', error.message);
    console.error('Ham yanıt:', error.response.data);
  }
}

Bir doğrulayıcı içinde fırlatılan BaseAdapterException dışındaki hatalar (örn. ZodError) otomatik olarak ValidationException içine sarılır; orijinal hata error.cause üzerinden erişilebilir:

typescript
} catch (error) {
  if (isValidationException<ZodError>(error) && error.cause) {
    console.error('Şema sorunları:', error.cause.issues);
  }
}

Released under the MIT License.