Synchronize Offline Mutations Using Axios

Synchronizing offline mutations with Axios requires a resilient architecture based on request interception, persistent queuing, and sequential execution upon reconnection. This article outlines the optimal strategy for capturing write operations (POST, PUT, PATCH, DELETE) when a user is offline, persisting them locally, and reliably replaying them using Axios interceptors and background sync mechanisms while preventing race conditions and data conflicts.

1. Intercepting and Storing Mutations

The foundation of offline synchronization is identifying when a network mutation fails due to connectivity loss. Axios response interceptors provide the most effective hook for this purpose.

axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const { config } = error;
    const isMutation = config && ['post', 'put', 'patch', 'delete'].includes(config.method.toLowerCase());
    
    if (!error.response && isMutation) {
      await addToOfflineQueue({
        id: crypto.randomUUID(),
        url: config.url,
        method: config.method,
        data: config.data,
        headers: config.headers,
        timestamp: Date.now()
      });
      return Promise.reject(new Error('Offline: Mutation queued for sync.'));
    }
    return Promise.reject(error);
  }
);

2. Network State Monitoring and Synchronization Triggers

Replay should only be initiated when the application regains a stable internet connection.

3. Sequential Queue Processing (FIFO)

Mutations are often interdependent (for example, creating a resource and subsequently updating it). Executing offline requests in parallel can lead to race conditions and 404/409 errors.

async function processQueue() {
  const queue = await getOfflineQueue(); // Sorted by timestamp ASC
  
  for (const item of queue) {
    try {
      await axios({
        url: item.url,
        method: item.method,
        data: item.data,
        headers: {
          ...item.headers,
          'X-Idempotency-Key': item.id // Prevent duplicate execution
        }
      });
      await removeFromOfflineQueue(item.id);
    } catch (error) {
      if (error.response && error.response.status >= 400 && error.response.status < 500) {
        // Handle unrecoverable client errors (e.g., validation failed)
        await logSyncError(item, error);
        await removeFromOfflineQueue(item.id);
      } else {
        // Network failure or server error: stop processing to retry later
        break;
      }
    }
  }
}

4. Idempotency and Conflict Resolution

Network transitions often cause requests to reach the server without the client receiving the confirmation.