JAVASCRIPT

Ensure Transactional Safety with Idempotent API Requests (Node.js)

Implement idempotent API requests in Node.js by adding a unique identifier, allowing safe retries of operations without unintended duplicate processing.

const axios = require('axios');
const { v4: uuidv4 } = require('uuid'); // npm install uuid

/**
 * Makes an API request with an idempotency key.
 * Ensures that if the same request is sent multiple times due to network issues,
 * it will only be processed once by the server.
 *
 * @param {string} url The API endpoint URL.
 * @param {string} method The HTTP method (e.g., 'POST', 'PUT').
 * @param {object} data The request payload.
 * @param {string} idempotencyKey Optional. A unique key for this request. If not provided, one will be generated.
 * @returns {Promise<object>} The API response data.
 */
async function makeIdempotentRequest(url, method, data, idempotencyKey = uuidv4()) {
  try {
    const headers = {
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey, // Crucial header for idempotency
      // Add other headers like Authorization if needed
      // 'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
    };

    const response = await axios({
      method: method,
      url: url,
      data: data,
      headers: headers
    });

    console.log(`Idempotent request successful to ${url}. Status: ${response.status}`);
    return response.data;
  } catch (error) {
    console.error(`Idempotent request failed to ${url}: ${error.message}`);
    // Handle specific error codes related to idempotency (e.g., 409 Conflict if server explicitly indicates a duplicate)
    if (error.response && error.response.status === 409) {
      console.warn('Request was potentially a duplicate and already processed.');
    }
    throw error; // Re-throw the error for upstream handling
  }
}

// Example Usage:
async function processOrder(orderData) {
  const orderId = orderData.id || uuidv4(); // Use an existing order ID or generate a new one
  const uniqueActionKey = `create-order-${orderId}`; // Specific key for this action
  try {
    console.log(`Attempting to process order with idempotency key: ${uniqueActionKey}`);
    const result = await makeIdempotentRequest(
      'https://api.example.com/orders', // Replace with your actual API endpoint
      'POST',
      orderData,
      uniqueActionKey
    );
    console.log('Order processing result:', result);
    return result;
  } catch (e) {
    console.error('Failed to process order:', e.message);
    throw e;
  }
}

// Simulate calling the function
// const myOrder = { id: 'order-123', item: 'Widget A', quantity: 2, amount: 29.99 };
// processOrder(myOrder);
// // If network error, you could retry with the same orderId and uniqueActionKey
// setTimeout(() => processOrder(myOrder), 5000); // Simulate a retry after 5 seconds
How it works: This Node.js snippet shows how to implement idempotent API requests, a best practice for operations like creating or updating resources, where retries might accidentally duplicate data. It generates a unique `Idempotency-Key` (using `uuid` for simplicity) and includes it in the request headers. If the server is designed to support idempotency, it will use this key to ensure the operation is executed only once, even if the request is sent multiple times.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs