---
title: "How would you implement offline-first data sync with IndexedDB and a remote API?"  
description: "How would you implement offline-first data sync with IndexedDB and a remote API?"  
author: "ICSM Computer"  
published: 2025-07-09  
updated: 2025-07-09  
canonical: https://www.mindstick.com/interview/34329/how-would-you-implement-offline-first-data-sync-with-indexeddb-and-a-remote-api  
category: "IndexedDB"  
tags: ["indexeddb"]  
reading_time: 5 minutes  

---

# How would you implement offline-first data sync with IndexedDB and a remote API?

To implement [**offline-first data sync**](https://www.mindstick.com/interview/34298/how-do-you-handle-offline-data-sync-using-indexeddb) with [**IndexedDB**](https://www.mindstick.com/interview/34290/why-use-indexeddb) and a **remote API**, follow a clear pattern: **queue, store, detect, and sync**. Here's a robust and scalable approach, step-by-step:

## 1. Architecture Overview

```plaintext
[UI] → writes/reads
 ↓             ↑
[IndexedDB] ←→ [Sync Queue]
       ↑           ↓
    [Sync Engine] (online)
          ↓
      [Remote API]
```

## 2. Core Components

| Component | Responsibility |
| --- | --- |
| IndexedDB | Stores app data + write queue locally |
| Sync Queue | Temporarily holds offline changes |
| Remote API | Source of truth (backend) |
| Sync Engine | Detects connectivity and syncs when online |

## 3. Implementation Steps

### Step 1: Set Up Dexie.js (or plain IndexedDB)

```javascript
const db = new Dexie('AppDB');
db.version(1).stores({
  items: '++id, name, updatedAt',
  syncQueue: '++id, operation, table, payload, timestamp'
});
```

- `items`: Your main data table
- `syncQueue`: Holds `add`, `update`, `delete` ops while offline

### Step 2: Queue Writes While Offline

Instead of calling the API directly, queue the change locally:

```javascript
async function addItemOffline(item) {
  await db.items.add(item);

  await db.syncQueue.add({
    operation: 'add',
    table: 'items',
    payload: item,
    timestamp: Date.now()
  });
}
```

### Step 3: Detect Online/Offline Status

```javascript
window.addEventListener('online', syncWithServer);
```

Or manually:

```javascript
if (navigator.onLine) {
  syncWithServer();
}
```

### Step 4: Sync Engine

```javascript
async function syncWithServer() {
  const queue = await db.syncQueue.orderBy('timestamp').toArray();

  for (const q of queue) {
    try {
      // Post to remote API
      await fetch(`/api/${q.table}/${q.operation}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(q.payload)
      });

      // Remove from queue if successful
      await db.syncQueue.delete(q.id);
    } catch (err) {
      console.error("Sync failed:", err);
      break; // Stop on first failure
    }
  }
}
```

### Step 5: Pull Updates from Server (Optional)

To keep IndexedDB fresh:

```javascript
async function pullLatest() {
  const res = await fetch('/api/items');
  const items = await res.json();
  await db.items.clear();
  await db.items.bulkPut(items);
}
```

Run this on:

- First app load
- Periodic intervals
- After successful sync

## Advanced Enhancements

| Feature | How |
| --- | --- |
| **Conflict resolution** | Use timestamps or versioning (e.g., `updatedAt`) |
| **Sync retries** | Retry failed syncs with exponential backoff |
| **Background sync (PWA)** | Use Service Workers + Background Sync API |
| **Delta sync** | Send only changes, not full lists |
| **Status UI** | Show "Queued changes" or "Offline mode" indicators |

## Data Flow Summary

```plaintext
User adds data → stored in IndexedDB + syncQueue
Goes online → `syncQueue` processed and pushed to API
Server updates pulled back → `items` table updated
```

## Tools You Can Use

| Tool | Purpose |
| --- | --- |
| **Dexie.js** | Simplified IndexedDB |
| **localForage** | Unified local storage (IndexedDB fallback) |
| **RxDB** / **PouchDB** | Pre-built sync with CouchDB-like APIs |
| **Workbox** | Background sync + caching for PWAs |

## Answers

### Answer by ICSM Computer

To implement [**offline-first data sync**](https://www.mindstick.com/interview/34298/how-do-you-handle-offline-data-sync-using-indexeddb) with [**IndexedDB**](https://www.mindstick.com/interview/34290/why-use-indexeddb) and a **remote API**, follow a clear pattern: **queue, store, detect, and sync**. Here's a robust and scalable approach, step-by-step:

## 1. Architecture Overview

```plaintext
[UI] → writes/reads
 ↓             ↑
[IndexedDB] ←→ [Sync Queue]
       ↑           ↓
    [Sync Engine] (online)
          ↓
      [Remote API]
```

## 2. Core Components

| Component | Responsibility |
| --- | --- |
| IndexedDB | Stores app data + write queue locally |
| Sync Queue | Temporarily holds offline changes |
| Remote API | Source of truth (backend) |
| Sync Engine | Detects connectivity and syncs when online |

## 3. Implementation Steps

### Step 1: Set Up Dexie.js (or plain IndexedDB)

```javascript
const db = new Dexie('AppDB');
db.version(1).stores({
  items: '++id, name, updatedAt',
  syncQueue: '++id, operation, table, payload, timestamp'
});
```

- `items`: Your main data table
- `syncQueue`: Holds `add`, `update`, `delete` ops while offline

### Step 2: Queue Writes While Offline

Instead of calling the API directly, queue the change locally:

```javascript
async function addItemOffline(item) {
  await db.items.add(item);

  await db.syncQueue.add({
    operation: 'add',
    table: 'items',
    payload: item,
    timestamp: Date.now()
  });
}
```

### Step 3: Detect Online/Offline Status

```javascript
window.addEventListener('online', syncWithServer);
```

Or manually:

```javascript
if (navigator.onLine) {
  syncWithServer();
}
```

### Step 4: Sync Engine

```javascript
async function syncWithServer() {
  const queue = await db.syncQueue.orderBy('timestamp').toArray();

  for (const q of queue) {
    try {
      // Post to remote API
      await fetch(`/api/${q.table}/${q.operation}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(q.payload)
      });

      // Remove from queue if successful
      await db.syncQueue.delete(q.id);
    } catch (err) {
      console.error("Sync failed:", err);
      break; // Stop on first failure
    }
  }
}
```

### Step 5: Pull Updates from Server (Optional)

To keep IndexedDB fresh:

```javascript
async function pullLatest() {
  const res = await fetch('/api/items');
  const items = await res.json();
  await db.items.clear();
  await db.items.bulkPut(items);
}
```

Run this on:

- First app load
- Periodic intervals
- After successful sync

## Advanced Enhancements

| Feature | How |
| --- | --- |
| **Conflict resolution** | Use timestamps or versioning (e.g., `updatedAt`) |
| **Sync retries** | Retry failed syncs with exponential backoff |
| **Background sync (PWA)** | Use Service Workers + Background Sync API |
| **Delta sync** | Send only changes, not full lists |
| **Status UI** | Show "Queued changes" or "Offline mode" indicators |

## Data Flow Summary

```plaintext
User adds data → stored in IndexedDB + syncQueue
Goes online → `syncQueue` processed and pushed to API
Server updates pulled back → `items` table updated
```

## Tools You Can Use

| Tool | Purpose |
| --- | --- |
| **Dexie.js** | Simplified IndexedDB |
| **localForage** | Unified local storage (IndexedDB fallback) |
| **RxDB** / **PouchDB** | Pre-built sync with CouchDB-like APIs |
| **Workbox** | Background sync + caching for PWAs |


---

Original Source: https://www.mindstick.com/interview/34329/how-would-you-implement-offline-first-data-sync-with-indexeddb-and-a-remote-api

Copyright © MindStick Software Pvt. Ltd. This Markdown version is provided for developers, AI systems, and offline reading.
