Skip to content

idempotent

awekrx edited this page May 29, 2026 · 1 revision

idempotent

Import

import { idempotent } from '@dev-suite/decorators/idempotent'

Category

  • method

Use Case

Reuse the same in-flight result for the same idempotency key.

Replaces

  • Custom idempotency maps and cleanup logic
  • Ad-hoc duplicate-submit protection

Example 1

Without decorator

class PayoutService {
  private requests = new Map<string, Promise<PayoutResult>>();

  async send(key: string, payload: PayoutInput) {
    if (!this.requests.has(key)) {
      this.requests.set(key, this.gateway.send(payload).finally(() => this.requests.delete(key)));
    }
    return this.requests.get(key)!;
  }
}

With decorator

import { idempotent } from '@dev-suite/decorators/idempotent';

class PayoutService {
  @idempotent({ keyResolver: ([key]) => String(key) })
  async send(key: string, payload: PayoutInput) {
    return this.gateway.send(payload);
  }
}

Why better

  • Guarantees consistent key-based deduplication for retries/click spam.
  • No low-level map management in method.

Example 2

Without decorator

class CheckoutService {
  private inFlight = new Map<string, Promise<Order>>();

  async placeOrder(req: PlaceOrderRequest) {
    const key = req.idempotencyKey;
    if (!this.inFlight.has(key)) {
      this.inFlight.set(key, this.orders.create(req).finally(() => this.inFlight.delete(key)));
    }
    return this.inFlight.get(key)!;
  }
}

With decorator

import { idempotent } from '@dev-suite/decorators/idempotent';

class CheckoutService {
  @idempotent({ keyResolver: ([req]) => String(req.idempotencyKey) })
  async placeOrder(req: PlaceOrderRequest) {
    return this.orders.create(req);
  }
}

Why better

  • Works with rich request payloads via keyResolver.
  • Centralized idempotency strategy across flows.

Clone this wiki locally