OFFLINE-FIRST REAL-TIME INDEXEDDB WEBSOCKETS DATA-SYNCHRONIZATION WEB-DEVELOPMENT FRONTEND PWA DATA-PERSISTENCE BROWSER-API USER-EXPERIENCE RESILIENCE CONFLICT-RESOLUTION

Membangun Aplikasi Offline-First & Real-time dengan IndexedDB dan WebSockets: Pola Sinkronisasi Data yang Tangguh

⏱️ 7 menit baca
👨‍💻

Membangun Aplikasi Offline-First & Real-time dengan IndexedDB dan WebSockets: Pola Sinkronisasi Data yang Tangguh

Pernahkah Anda menggunakan aplikasi chat, to-do list, atau editor dokumen online yang tetap berfungsi mulus meskipun koneksi internet Anda tiba-tiba terputus? Begitu terhubung kembali, semua perubahan Anda langsung tersinkronisasi tanpa drama. Itulah keajaiban kombinasi Offline-First dan Real-time.

Di era web modern, ekspektasi pengguna terhadap aplikasi sangat tinggi. Mereka menginginkan aplikasi yang cepat, responsif, dan selalu tersedia, terlepas dari kondisi jaringan. Membangun aplikasi yang hanya berfungsi saat online saja sudah tidak relevan. Begitu pula, aplikasi yang tidak memberikan update instan saat online akan terasa ketinggalan zaman.

Artikel ini akan memandu Anda bagaimana menggabungkan dua kekuatan besar di browser – IndexedDB untuk persistensi data offline yang tangguh, dan WebSockets untuk komunikasi real-time yang efisien – guna menciptakan aplikasi web yang benar-benar adaptif dan memberikan pengalaman pengguna yang superior.

1. Pendahuluan: Mengapa Offline-First dan Real-time Bersama?

Bayangkan skenario ini: Anda sedang mengerjakan laporan penting di aplikasi web. Tiba-tiba, Wi-Fi mati. Jika aplikasi Anda tidak mendukung offline-first, semua pekerjaan Anda bisa hilang, atau Anda terblokir total. Namun, jika aplikasi Anda offline-first, Anda bisa terus bekerja. Saat koneksi kembali, perubahan Anda akan dikirim ke server.

Sekarang, tambahkan elemen real-time. Jika Anda berkolaborasi dengan tim, Anda ingin melihat perubahan yang dibuat rekan kerja Anda secara instan, tanpa harus refresh halaman.

Menggabungkan Offline-First dan Real-time berarti membangun aplikasi yang:

Tantangannya adalah menjaga konsistensi data antara klien (browser) dan server, serta antar klien yang berbeda, terutama saat ada periode offline dan potensi konflik. Di sinilah IndexedDB dan WebSockets menjadi pasangan yang sempurna.

2. Fondasi Offline: Menguasai IndexedDB

IndexedDB adalah database NoSQL berbasis objek di sisi klien, dirancang untuk menyimpan data terstruktur dalam jumlah besar di browser. Ini jauh lebih powerful daripada localStorage atau sessionStorage yang hanya mendukung penyimpanan string dalam jumlah terbatas.

📌 Mengapa IndexedDB?

💡 Konsep Dasar IndexedDB

IndexedDB bekerja dengan konsep database, object store (mirip tabel), dan index. Anda berinteraksi dengannya melalui request dan transaction.

// Contoh sederhana membuka database dan menambahkan data
function openDatabase() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('MyOfflineAppDB', 1);

    request.onupgradeneeded = event => {
      const db = event.target.result;
      if (!db.objectStoreNames.contains('todos')) {
        const objectStore = db.createObjectStore('todos', { keyPath: 'id', autoIncrement: true });
        objectStore.createIndex('status', 'status', { unique: false });
      }
    };

    request.onsuccess = event => resolve(event.target.result);
    request.onerror = event => reject('Error opening database: ' + event.target.errorCode);
  });
}

async function addTodo(todoText, status = 'pending') {
  const db = await openDatabase();
  const transaction = db.transaction(['todos'], 'readwrite');
  const objectStore = transaction.objectStore('todos');

  return new Promise((resolve, reject) => {
    const request = objectStore.add({ text: todoText, status: status, timestamp: Date.now() });

    request.onsuccess = event => resolve(event.target.result);
    request.onerror = event => reject('Error adding todo: ' + event.target.errorCode);
  });
}

// Penggunaan
// addTodo("Belajar IndexedDB", "pending").then(id => console.log("Todo added with ID:", id));

Catatan: Untuk aplikasi sungguhan, pertimbangkan library pembungkus seperti Dexie.js atau localforage yang menyederhanakan API IndexedDB yang cukup verbose.

3. Fondasi Real-time: Kekuatan WebSockets

WebSockets menyediakan saluran komunikasi full-duplex (dua arah) yang persisten antara klien (browser) dan server. Berbeda dengan HTTP request/response tradisional, setelah koneksi WebSocket terjalin, server dapat “mendorong” data ke klien kapan saja, dan sebaliknya.

📌 Mengapa WebSockets?

💡 Konsep Dasar WebSockets

// Contoh sederhana koneksi WebSocket di sisi klien
const socket = new WebSocket('ws://localhost:8080'); // Ganti dengan URL server WebSocket Anda

socket.onopen = (event) => {
  console.log('Koneksi WebSocket terbuka:', event);
  socket.send('Halo dari klien!');
};

socket.onmessage = (event) => {
  console.log('Menerima pesan dari server:', event.data);
  // Di sini Anda akan memproses update dari server
  // Misalnya, mengupdate IndexedDB dan UI
};

socket.onclose = (event) => {
  if (event.wasClean) {
    console.log(`Koneksi ditutup dengan rapi, kode=${event.code} alasan=${event.reason}`);
  } else {
    // Misalnya server mati atau koneksi jaringan putus
    console.error('Koneksi terputus secara tidak terduga');
    // Implementasi retry/reconnection di sini
  }
};

socket.onerror = (error) => {
  console.error('WebSocket Error:', error);
};

// Fungsi untuk mengirim pesan
function sendUpdateToServer(data) {
  if (socket.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify(data));
  } else {
    console.warn('WebSocket tidak terbuka, antrekan pesan untuk dikirim nanti.');
    // Di sini Anda akan mengantrekan perubahan yang belum terkirim
  }
}

// Penggunaan
// sendUpdateToServer({ type: 'ADD_TODO', payload: { text: 'Beli kopi', status: 'pending' } });

4. Strategi Sinkronisasi Data: Offline-First Meets Real-time

Menggabungkan IndexedDB dan WebSockets membutuhkan strategi sinkronisasi yang cerdas. Berikut adalah pola umum yang bisa Anda terapkan:

✅ Pola “Push-Pull” dengan Queue

Ini adalah pendekatan yang paling umum dan tangguh:

  1. Perubahan Lokal (Offline/Online):

    • Ketika pengguna melakukan perubahan (misalnya, membuat todo baru, mengedit todo yang ada), perubahan tersebut segera disimpan ke IndexedDB dan UI diperbarui secara optimis.
    • Perubahan ini juga ditandai sebagai “pending sync” atau dimasukkan ke dalam “sync queue” di IndexedDB.
    • Jika aplikasi offline, queue ini akan terus menampung perubahan.
  2. Mendeteksi Koneksi Online:

    • Gunakan navigator.onLine dan event online/offline pada window untuk mendeteksi status koneksi.
    • Saat aplikasi mendeteksi kembali online:
      • Coba bangun kembali koneksi WebSocket jika terputus.
      • Ambil semua perubahan yang “pending sync” dari IndexedDB.
      • Kirim perubahan ini ke server melalui WebSocket secara berurutan.
  3. Server Menerima dan Memproses Perubahan:

    • Server menerima perubahan dari klien.
    • Validasi dan proses perubahan, lalu simpan ke database server.
    • Setelah berhasil, server mengirimkan “konfirmasi” kembali ke klien pengirim dan juga “mendorong” update ke semua klien yang terhubung (termasuk klien pengirim itu sendiri) melalui WebSockets.
  4. Klien Menerima Update dari Server:

    • Ketika klien menerima update dari server (baik konfirmasi dari perubahannya sendiri atau perubahan dari klien lain):
      • Update data di IndexedDB klien.
      • Perbarui UI klien untuk merefleksikan data terbaru.
      • Jika update adalah konfirmasi dari perubahan “pending sync” yang dikirim klien itu sendiri, tandai perubahan tersebut sebagai “sudah tersinkronisasi” di IndexedDB dan hapus dari sync queue.

🎯 Optimistic UI

Optimistic UI adalah kunci untuk pengalaman pengguna yang mulus. Daripada menunggu konfirmasi server, aplikasi segera menampilkan hasil dari aksi pengguna. Jika server mengembalikan error, UI bisa “rollback” atau menampilkan pesan error. Ini harus digabungkan dengan pola push-pull di atas.

⚠️ Penanganan Konflik

Ini adalah bagian tersulit dalam sinkronisasi: