idempotency

Idempotency concepts, patterns for APIs REST, database operations, and implementation strategies. Use when implementing idempotent operations, handling retries, or when ensuring operations can be safely repeated in any language.

16 stars

Best use case

idempotency is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Idempotency concepts, patterns for APIs REST, database operations, and implementation strategies. Use when implementing idempotent operations, handling retries, or when ensuring operations can be safely repeated in any language.

Teams using idempotency should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/idempotency/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/idempotency/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/idempotency/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How idempotency Compares

Feature / AgentidempotencyStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Idempotency concepts, patterns for APIs REST, database operations, and implementation strategies. Use when implementing idempotent operations, handling retries, or when ensuring operations can be safely repeated in any language.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Idempotency Best Practices

This skill provides idempotency concepts, patterns, and implementation strategies applicable across multiple programming languages and frameworks.

## Core Concepts

### What is Idempotency?

An operation is **idempotent** if performing it multiple times produces the same result as performing it once.

**Mathematical Example:**
- `f(f(x)) = f(x)`
- Example: `abs(abs(-5)) = abs(-5) = 5`

**Programming Example:**
- GET request: Always returns same result
- DELETE request: Deleting twice has same effect as once
- PUT request: Replacing resource multiple times has same effect

## Why Idempotency Matters

1. **Retry Safety**: Network failures can cause retries - operations must handle this
2. **Distributed Systems**: Multiple services may attempt same operation
3. **User Experience**: Users may click "Submit" multiple times
4. **Reliability**: Ensures system consistency under failures

## HTTP Methods and Idempotency

### Idempotent Methods

**GET**: Always safe and idempotent
- Reading data doesn't change state
- Can be called multiple times safely

**PUT**: Idempotent (when used correctly)
- Replacing resource with same data multiple times has same effect
- Must include complete resource representation

**DELETE**: Idempotent
- Deleting already-deleted resource has same effect (no-op)

### Non-Idempotent Methods

**POST**: Not idempotent
- Creates new resources
- Each call may create different resource

**PATCH**: Not idempotent (usually)
- Partial updates may have different effects on repeated calls

## API Idempotency Patterns

### Pattern 1: Idempotency Keys

Client provides unique key for each operation. Server uses key to detect duplicates.

```typescript
// Client sends request with idempotency key
POST /api/orders
Headers:
  Idempotency-Key: abc123-def456-ghi789

// Server implementation
const idempotencyKey = req.headers['idempotency-key'];

if (idempotencyKey) {
  // Check if we've seen this key before
  const cachedResponse = await cache.get(`idempotency:${idempotencyKey}`);
  if (cachedResponse) {
    return res.status(cachedResponse.status).json(cachedResponse.body);
  }
  
  // Process request
  const order = await createOrder(req.body);
  
  // Cache response
  await cache.set(`idempotency:${idempotencyKey}`, {
    status: 201,
    body: order,
  }, { ttl: 24 * 60 * 60 }); // 24 hours
  
  return res.status(201).json(order);
}
```

### Pattern 2: Request Deduplication

```typescript
class IdempotencyHandler {
  async handleRequest(
    idempotencyKey: string,
    handler: () => Promise<any>
  ): Promise<any> {
    // Check if request already processed
    const existing = await this.getResponse(idempotencyKey);
    if (existing) {
      return existing;
    }
    
    // Process request
    try {
      const result = await handler();
      await this.storeResponse(idempotencyKey, result);
      return result;
    } catch (error) {
      // Don't cache errors - allow retry
      throw error;
    }
  }
  
  private async getResponse(key: string) {
    return cache.get(`idempotency:${key}`);
  }
  
  private async storeResponse(key: string, response: any) {
    await cache.set(`idempotency:${key}`, response, { ttl: 86400 });
  }
}
```

## Database Idempotency Patterns

### Pattern 1: UPSERT Operations

```sql
-- PostgreSQL
INSERT INTO orders (id, user_id, total, status)
VALUES ($1, $2, $3, 'pending')
ON CONFLICT (id) DO UPDATE SET
  total = EXCLUDED.total,
  updated_at = NOW()
RETURNING *;
```

```typescript
// MongoDB
await db.orders.findOneAndUpdate(
  { id: orderId },
  { $set: { total, status: 'pending' } },
  { upsert: true, new: true }
);
```

### Pattern 2: Conditional Updates

```typescript
// Only update if version matches (optimistic locking)
async function updateOrder(orderId: string, data: Partial<Order>, expectedVersion: number) {
  const result = await db.orders.updateOne(
    { 
      id: orderId,
      version: expectedVersion, // Only update if version matches
    },
    {
      $set: { ...data, version: expectedVersion + 1 },
    }
  );
  
  if (result.matchedCount === 0) {
    throw new Error('Order was modified by another operation');
  }
  
  return result;
}
```

### Pattern 3: Unique Constraints

```sql
-- Prevent duplicate operations using unique constraint
CREATE UNIQUE INDEX idx_order_user_unique 
ON orders(user_id, order_number);

-- Insert will fail if duplicate exists
INSERT INTO orders (user_id, order_number, total)
VALUES ($1, $2, $3);
-- If duplicate, operation is idempotent (no change)
```

## Implementation Strategies

### Strategy 1: Versioning

Use version numbers or timestamps to detect changes.

```typescript
interface Resource {
  id: string;
  version: number;
  data: any;
}

async function updateResource(id: string, data: any, expectedVersion: number) {
  const resource = await getResource(id);
  
  if (resource.version !== expectedVersion) {
    throw new Error('Resource was modified');
  }
  
  return updateResourceWithVersion(id, data, expectedVersion + 1);
}
```

### Strategy 2: State Machines

Use state machines to ensure operations only happen in valid states.

```typescript
enum OrderStatus {
  PENDING = 'pending',
  PROCESSING = 'processing',
  COMPLETED = 'completed',
  CANCELLED = 'cancelled',
}

async function processOrder(orderId: string) {
  const order = await getOrder(orderId);
  
  // Only process if in pending state
  if (order.status !== OrderStatus.PENDING) {
    return order; // Idempotent: return current state
  }
  
  // Update to processing
  await updateOrderStatus(orderId, OrderStatus.PROCESSING);
  
  try {
    await fulfillOrder(order);
    await updateOrderStatus(orderId, OrderStatus.COMPLETED);
  } catch (error) {
    await updateOrderStatus(orderId, OrderStatus.CANCELLED);
    throw error;
  }
}
```

### Strategy 3: Tokens/Request IDs

Generate unique tokens for each operation attempt.

```typescript
async function createPayment(paymentData: PaymentData, requestId: string) {
  // Check if this request was already processed
  const existing = await db.payments.findOne({ requestId });
  if (existing) {
    return existing; // Return existing payment (idempotent)
  }
  
  // Create new payment with request ID
  const payment = await db.payments.create({
    ...paymentData,
    requestId,
    status: 'pending',
  });
  
  return payment;
}
```

## Best Practices

### ✅ DO:

1. **Always Return Same Result**: Same input → same output
2. **Use Idempotency Keys**: For POST/PATCH operations
3. **Check Before Creating**: Verify resource doesn't exist
4. **Use UPSERT**: For create-or-update operations
5. **Log Idempotent Operations**: Track for debugging
6. **Handle Retries**: Design for automatic retries
7. **Version Resources**: Use versioning for updates

### ❌ DON'T:

1. **Increment Counters**: Without checking current value
2. **Create Multiple Resources**: From same operation
3. **Side Effects**: Uncontrolled side effects
4. **Ignore Duplicates**: Always check for existing operations
5. **Cache Errors**: Don't cache error responses as idempotent

## Common Patterns

### Pattern: Idempotent Create

```typescript
async function createOrder(orderData: OrderData, idempotencyKey: string) {
  // Check if order with this key already exists
  const existing = await db.orders.findOne({ idempotencyKey });
  if (existing) {
    return existing; // Return existing order
  }
  
  // Create new order
  const order = await db.orders.create({
    ...orderData,
    idempotencyKey,
  });
  
  return order;
}
```

### Pattern: Idempotent Update

```typescript
async function updateOrder(orderId: string, updates: Partial<Order>) {
  // Get current order
  const order = await db.orders.findById(orderId);
  
  // Check if update would change anything
  const hasChanges = Object.keys(updates).some(
    key => order[key] !== updates[key]
  );
  
  if (!hasChanges) {
    return order; // No changes, return current state (idempotent)
  }
  
  // Apply updates
  return db.orders.update(orderId, updates);
}
```

## Key Principles

1. **Same Input, Same Output**: Idempotent operations always return same result
2. **Safe to Retry**: Operations can be safely retried
3. **Check Before Act**: Verify state before performing operation
4. **Use Idempotency Keys**: For non-idempotent HTTP methods
5. **Version Resources**: Track changes to detect conflicts
6. **State Machines**: Use states to control operation flow
7. **Log Operations**: Track idempotent operations for debugging

Related Skills

Idempotency And Dedup

16
from diegosouzapw/awesome-omni-skill

Idempotency and Deduplication are strategies for ensuring operations can be safely retried without causing unintended side effects. Idempotent operations produce the same result regardless of how many

bgo

10
from diegosouzapw/awesome-omni-skill

Automates the complete Blender build-go workflow, from building and packaging your extension/add-on to removing old versions, installing, enabling, and launching Blender for quick testing and iteration.

Coding & Development

javascript-typescript-typescript-scaffold

16
from diegosouzapw/awesome-omni-skill

You are a TypeScript project architecture expert specializing in scaffolding production-ready Node.js and frontend applications. Generate complete project structures with modern tooling (pnpm, Vite, N

javascript-testing-patterns

16
from diegosouzapw/awesome-omni-skill

Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use...

javascript-pro

16
from diegosouzapw/awesome-omni-skill

Master modern JavaScript with ES6+, async patterns, and Node.js APIs. Handles promises, event loops, and browser/Node compatibility.

java-pro

16
from diegosouzapw/awesome-omni-skill

Master Java 21+ with modern features like virtual threads, pattern matching, and Spring Boot 3.x. Expert in the latest Java ecosystem including GraalVM, Project Loom, and cloud-native patterns. Use PROACTIVELY for Java development, microservices architecture, or performance optimization.

java-doctor

16
from diegosouzapw/awesome-omni-skill

Comprehensive Java code health analyzer. 0-100 score with diagnostics. Progressive loading - detects project tech (Spring, gRPC, JPA) and loads relevant rules. Version-aware for Java 8-25 & Spring Boot 3.x/4.x. Dead code detection included. Use when reviewing Java code, finding bugs, or preparing for PR.

java-dev

16
from diegosouzapw/awesome-omni-skill

Java 开发规范,包含命名约定、异常处理、Spring Boot 最佳实践等

java-21-to-java-25-upgrade

16
from diegosouzapw/awesome-omni-skill

Comprehensive best practices for adopting new Java 25 features since the release of Java 21. Triggers on: ['*']

iroh-p2p

16
from diegosouzapw/awesome-omni-skill

Build modern peer-to-peer applications with Iroh. QUIC-based P2P networking, hole punching, content distribution, and decentralized data synchronization.

irish-takeaway

16
from diegosouzapw/awesome-omni-skill

Find nearby takeaways in Ireland and browse menus via Deliveroo/Just Eat. Uses Google Places API for discovery and browser automation for menu scraping.

ipai-ui-ux-pro-max

16
from diegosouzapw/awesome-omni-skill

Design intelligence for UI/UX generation and critique; use for layouts, typography, color palettes, tokens, and UX best practices. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient. Integrations: shadcn/ui MCP for component search and examples.