# Database-Based Idempotency ## Database-Based Idempotency ```typescript import { Pool } from "pg"; interface IdempotencyRecord { key: string; request_body: any; response_body?: any; status: string; error_message?: string; created_at: Date; completed_at?: Date; } class DatabaseIdempotency { constructor(private db: Pool) { this.createTable(); } private async createTable(): Promise { await this.db.query(` CREATE TABLE IF NOT EXISTS idempotency_keys ( key VARCHAR(255) PRIMARY KEY, request_body JSONB NOT NULL, response_body JSONB, status VARCHAR(50) NOT NULL, error_message TEXT, created_at TIMESTAMP DEFAULT NOW(), completed_at TIMESTAMP, expires_at TIMESTAMP NOT NULL ); CREATE INDEX IF NOT EXISTS idx_idempotency_expires ON idempotency_keys (expires_at); `); } async checkIdempotency( key: string, requestBody: any, ): Promise { const result = await this.db.query( "SELECT * FROM idempotency_keys WHERE key = $1", [key], ); if (result.rows.length === 0) { return null; } const record = result.rows[0]; // Check if request body matches if (JSON.stringify(record.request_body) !== JSON.stringify(requestBody)) { throw new Error("Request body mismatch for idempotency key"); } return record; } async startProcessing(key: string, requestBody: any): Promise { try { const expiresAt = new Date(Date.now() + 86400 * 1000); // 24 hours await this.db.query( ` INSERT INTO idempotency_keys (key, request_body, status, expires_at) VALUES ($1, $2, 'processing', $3) `, [key, requestBody, expiresAt], ); return true; } catch (error: any) { if (error.code === "23505") { // Unique violation return false; } throw error; } } async completeRequest(key: string, responseBody: any): Promise { await this.db.query( ` UPDATE idempotency_keys SET response_body = $1, status = 'completed', completed_at = NOW() WHERE key = $2 `, [responseBody, key], ); } async failRequest(key: string, errorMessage: string): Promise { await this.db.query( ` UPDATE idempotency_keys SET error_message = $1, status = 'failed', completed_at = NOW() WHERE key = $2 `, [errorMessage, key], ); } async cleanup(): Promise { const result = await this.db.query(` DELETE FROM idempotency_keys WHERE expires_at < NOW() `); return result.rowCount || 0; } } ```