Kontrak GraphQL menjaga agar webhook tetap idempoten dan retry-safe dengan menyamakan ekspektasi schema, metadata, dan autentikasi di antara pengirim dan penerima. Jika schema, directive, dan metadata ditetapkan secara eksplisit, penerima dapat mengenali operasi mana yang boleh dijalankan ulang tanpa efek samping.

Mengapa Kontrak GraphQL Penting untuk Webhook Idempoten

Webhooks mengandalkan HTTP POST berisi payload. Tanpa kontrak, retry otomatis atau manual dapat menyebabkan efek samping ganda karena penerima tidak tahu apakah event sudah diproses. Dengan kontrak GraphQL, pengirim menentukan jenis mutation, field metadata seperti eventId dan attempt, serta aturan idempoten melalui directive custom sehingga penerima bisa menerapkan deduplikasi, validasi, dan signing secara konsisten.

Merancang Schema GraphQL untuk Webhook Idempoten

Berikut contoh schema SDL yang menjelaskan kontrak webhook:

directive @webhookContract(idempotent: Boolean!, signed: Boolean!, metadata: [String!]!) on FIELD_DEFINITION

type WebhookPayload {
  eventId: ID!
  attempt: Int!
  data: JSON!
  signature: String
}

type Mutation {
  processOrderWebhook(input: WebhookPayload!): WebhookResponse @webhookContract(idempotent: true, signed: true, metadata: ["eventId", "attempt"])
}

Directive @webhookContract memberitahu penerima bahwa mutation tersebut bersifat idempoten, harus disign, dan metadata tertentu harus divalidasi. Metadata ini juga membantu middleware membangun header dan payload yang konsisten.

Metadata untuk Idempoten

Field seperti eventId, attempt, dan timestamp membantu menentukan apakah event sudah diproses. Metadata juga digunakan untuk pencatatan audit dan deduplikasi.

Resolver Pseudocode, Signing Auth, dan Validasi Payload

Resolver harus memeriksa metadata sebelum menjalankan bisnis logic:

function processOrderWebhookResolver(input) {
  if (!validateSignature(input.signature, input.data)) {
    throw new AuthorizationError('Signature invalid');
  }

  if (isDuplicate(input.eventId, input.attempt)) {
    return { status: 'duplicate' };
  }

  const payload = validatePayloadSchema(input.data);
  startTransaction();
  applyBusinessLogic(payload);
  recordProcessedEvent(input.eventId, input.attempt);
  commitTransaction();
  return { status: 'ok' };
}

Validasi payload terdiri dari schema checking (misalnya menggunakan JSON Schema atau Zod) agar mutation GraphQL tidak menerima data yang merusak. Penandatanganan bisa menggunakan HMAC-SHA256 dengan shared secret yang ada di header X-Webhook-Signature dan payload JSON.

Strategi Signing/Token

  • Gunakan secret yang tersimpan aman dan rotasi terjadwal.
  • Header X-Webhook-Timestamp membantu mencegah replay attack.
  • Token JWT atau HMAC cocok; periksa match antara signature dan payload.

Deduplikasi Event dan Retry Policy yang Terpercaya

Untuk menjamin idempoten, gunakan pola deduplikasi seperti menyimpan eventId dalam store cepat (Redis/DB) dengan TTL yang sesuai. Per-request idempotency key memegang pernyataan eksklusif bahwa event tertentu adalah satu-satunya versi yang diproses.

Retry policy penting karena event bisa dikirim ulang bila pemrosesan gagal atau tidak diakui. Kombinasikan backoff eksponensial dengan jitter untuk menghindari thundering herd:

maxRetries = 5
baseDelay = 1000 // ms
for attempt in 1..maxRetries:
  delay = baseDelay * (2 ** (attempt - 1))
  jitter = random(0, baseDelay)
  wait(delay + jitter)
  response = sendWebhook()
  if response.ok:
    break

Gambaran diagram teks:

send -> fail -> wait 1.2s -> retry -> fail -> wait 2.7s -> retry -> success
Gunakan attempt di payload sebagai metadata agar responder tahu urutan percobaan.

Per-request Idempotency Key

  • Header X-Idempotency-Key harus unik per event dan disimpan bersamaan status.
  • Jika event disetujui sebelumnya, kembalikan hasil cached tanpa menjalankan ulang.
  • Key idealnya digenerate di sisi pengirim sekaligus mencakup eventId dan timestamp.

Checklist Validasi Kontrak, Pengujian Webhook, dan Observabilitas

  • Validasi Kontrak: Pastikan schema SDL, directive, dan metadata dikomunikasikan ke tim pengirim dan penerima.
  • Pengujian: Gunakan stub GraphQL server dengan mutation test untuk memverifikasi idempotensi, signing, dan deduplikasi.
  • Observabilitas: Catat log/trace untuk setiap event, simpan eventId, status retry, dan waktu eksekusi.
  • Alert: Monitor kegagalan yang berulang, latensi retry, dan rate deduplikasi tinggi.

Checklist tambahan termasuk verifikasi TTL deduplikasi, threshold pemicu alert, dan simulasi ledakan retry bagi tim QA.

Penutup

Kontrak GraphQL untuk webhook memberikan dasar teknis bagi retry aman: schema yang jelas, directive custom, metadata idempoten, signing, deduplikasi, dan kebijakan retry eksponensial + jitter membuat integrasi lebih dapat diandalkan. Pastikan checklist validation, pengujian menyeluruh, serta observabilitas aktif guna mengawasi perilaku webhook secara nyata.