JAVASCRIPT
Ensure Idempotency for API Write Operations to Prevent Duplicates
Implement client-side strategies to make API write requests idempotent, preventing duplicate operations on the server side due to retries or network issues.
const axios = require('axios');
const { v4: uuidv4 } = require('uuid'); // Install with: npm install uuid
const API_PAYMENT_URL = 'https://api.your-payment-gateway.com/charges'; // Example payment endpoint
/**
* Creates a unique idempotency key for the request.
* This key should be generated once per logical operation.
*/
function generateIdempotencyKey() {
// A UUID is a common choice for an idempotency key.
// For requests involving a specific user/session, you might prefix or suffix it
// with a user ID or session ID for better server-side lookup, if applicable.
return uuidv4();
}
/**
* Makes an API call for a payment, ensuring idempotency.
* @param {object} paymentDetails - Details for the payment.
* @param {string} idempotencyKey - A unique key for this specific operation.
*/
async function processPayment(paymentDetails, idempotencyKey) {
try {
const response = await axios.post(
API_PAYMENT_URL,
paymentDetails,
{
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey, // Crucial header for idempotency
'Authorization': 'Bearer YOUR_AUTH_TOKEN', // Replace with actual auth
},
// Optional: you might want a timeout for requests that take too long
timeout: 10000, // 10 seconds
}
);
console.log('Payment processed successfully:', response.data);
return response.data;
} catch (error) {
if (error.response) {
console.error('Error processing payment (status:', error.response.status, 'data:', error.response.data);
// Servers might return specific status codes (e.g., 409 Conflict)
// to indicate an idempotent request was received and already processed.
if (error.response.status === 409) { // Example conflict status
console.warn('Payment with this idempotency key was likely already processed.');
}
} else {
console.error('Network or other error processing payment:', error.message);
}
throw new Error('Payment processing failed.');
}
}
// Example usage:
(async () => {
const paymentDetails = {
amount: 5000, // in cents
currency: 'usd',
customer_id: 'cust_12345',
payment_method_id: 'pm_card_visa',
description: 'Purchase of premium subscription',
};
const currentIdempotencyKey = generateIdempotencyKey();
console.log('Generated Idempotency Key:', currentIdempotencyKey);
try {
// First attempt to process payment
await processPayment(paymentDetails, currentIdempotencyKey);
console.log('First payment attempt completed.');
// Simulate a network issue or retry:
// If the first request timed out or failed *after* the server received it,
// retrying with the *same* idempotency key ensures the payment isn't duplicated.
console.log('
Simulating a retry with the same idempotency key...');
await processPayment(paymentDetails, currentIdempotencyKey);
console.log('Second payment attempt (retry) completed. Server should handle it idempotently.');
} catch (error) {
console.error('Overall process failed:', error.message);
}
})();
How it works: This Node.js snippet illustrates how to implement client-side idempotency for API write operations, particularly crucial for sensitive actions like payments or order creation. By generating a unique `Idempotency-Key` (typically a UUID) for each logical operation and including it in the request headers, the client signals to the server that multiple identical requests with the same key should be treated as a single operation. This prevents duplicate actions if the client retries a request due to network issues or timeouts, enhancing the reliability of API integrations.