Skip to main content
“Idempotent” bir istek, aynı girdi ile birden fazla kez gönderildiğinde yan etki yaratmayacak istektir. GET, PUT, DELETE doğası gereği idempotenttir — aynı URL’e tekrar istek atmak kaynak durumunu değiştirmez (veya kasten değiştirir ama tekrarlanabilir). POST ise genellikle yeni bir kaynak oluşturur; aynı POST’u iki kez gönderirseniz iki kayıt oluşur.
Flextell API şu anda Idempotency-Key header’ı gibi yerleşik bir idempotency mekanizmasını desteklemez. Bu sayfa, bu eksiğe rağmen güvenli yazma yapmanız için önerilen örüntüleri özetler. İleride resmi bir Idempotency-Key desteği eklenirse Changelog’da duyururuz.

Hangi istekler dikkat ister?

Aşağıdaki yazma uçları yan etki yaratır ve yanlışlıkla iki kez gönderilmesi hoş olmayan sonuçlar doğurabilir:
  • POST /v1/appointments — çift randevu
  • POST /v1/sales — çift satış kaydı
  • POST /v1/sales/{id}/payments — çift tahsilat
  • POST /v1/stocks/entry — çift stok girişi
  • POST /v1/stocks/adjustment — çift düzeltme
  • POST /v1/stocks/transfer — çift transfer
  • POST /v1/conversations/{id}/messages — aynı mesajın iki kere gönderilmesi

Örüntü 1 — İstemcide kilit (en basit)

Bir butonu “gönderim sırasında” devre dışı bırakın:
async function createSale(payload) {
  if (submitting) return; // çift tıklamayı engelle
  submitting = true;

  try {
    return await api.post("/v1/sales", payload);
  } finally {
    submitting = false;
  }
}
Bu, tek bir sekmedeki çift tıklamayı kapsar ama sunucu tarafı tekrar sorununu (istemci 500 aldı ama istek aslında işlendi) çözmez.

Örüntü 2 — Doğal anahtar ile kontrol

Veri modelinizin “doğal bir eşsiz anahtarı” varsa, isteği atmadan önce kaynağın zaten var olup olmadığını kontrol edin. Örnek: Randevu oluştururken (customer_id, starts_at, doctor_id) doğal olarak eşsizdir. Önce bir GET ile aynı kombinasyona sahip aktif randevu var mı kontrol edin; yoksa oluşturun.
async function ensureAppointment(args) {
  const existing = await api.get("/v1/appointments", {
    params: {
      customer_id: args.customer_id,
      from: args.starts_at,
      to: args.starts_at,
    },
  });

  if (existing.data.length > 0) {
    return existing.data[0];
  }

  return api.post("/v1/appointments", args);
}

Örüntü 3 — İstemci tarafı idempotency anahtarı

Kendi tarafınızda bir UUID üretip isteklerle eşleştirin. Aynı UUID için birden fazla gönderim olursa sunucuya atmadan önce yerel tablonuzdan önceki sonucu döndürün.
const localIdempotencyStore = new Map();

async function idempotentPost(uuid, url, body) {
  if (localIdempotencyStore.has(uuid)) {
    return localIdempotencyStore.get(uuid);
  }

  const pending = api.post(url, body);
  localIdempotencyStore.set(uuid, pending);

  try {
    return await pending;
  } catch (err) {
    localIdempotencyStore.delete(uuid); // hata durumunda yeniden denenebilsin
    throw err;
  }
}
Bu tek bir istemci oturumundaki duplikasyonu çözer ama farklı cihaz/oturumları korumaz.

Örüntü 4 — Sunucu yanıtını kontrol etme

Çağrı zaman aşımına uğradı veya ağ hatası verdi; istek gerçekten işlendi mi bilmiyorsunuz. Tekrar göndermeden önce kaynağı sorgulayın:
try {
  return await api.post("/v1/sales", payload);
} catch (err) {
  if (err instanceof NetworkTimeout) {
    // Son 5 dakikada aynı müşteri/tutar kombinasyonuna sahip satış oluştu mu?
    const recent = await api.get("/v1/sales", {
      params: { customer_id, from: fiveMinutesAgo, to: now },
    });
    const maybe = recent.data.find(sameShape(payload));
    if (maybe) return maybe;
  }
  throw err;
}

Öneriler

  • Para ve stok etkileyen uçlarda mutlaka bir deduplikasyon örüntüsü kullanın.
  • Mesaj gönderimi gibi yüksek hacimli uçlarda kullanıcıya “gönderiliyor…” göstergesi bırakın; ağ hatasında satırı “tekrar dene” butonuyla kullanıcıya bırakın.
  • Otomatik retry yalnızca 429, 5xx ve ağ hatalarında uygulanmalı; 4xx hatalarında isteği tekrarlamak sorunu çözmez.