Ketika data yang harus disimpan melampaui sekadar preferensi, misalnya ratusan catatan lengkap dengan tanggalnya, Web Storage mulai terasa sempit dan IndexedDB menjadi kelanjutannya. IndexedDB adalah basis data yang hidup di dalam browser, menyimpan object dalam jumlah besar, dapat dicari lewat index, dan bekerja secara asinkron sehingga halaman tidak pernah tertahan. Topik ini memperkenalkan susunan bagiannya beserta pola pembukaan database yang paling sering dipakai.
Kapan localStorage tidak cukup#
Web Storage menyimpan string dengan kapasitas kecil, umumnya sekitar lima megabita per origin, dan setiap operasinya berjalan sinkron dengan kode halaman. Batas itu terasa ketika isinya berupa ratusan object yang saling terkait, misalnya arsip catatan harian yang perlu dicari menurut tanggal. Menyimpan semuanya sebagai string JSON besar memaksa pembongkaran penuh setiap kali satu item saja dibutuhkan.
IndexedDB menjawab kekurangan tersebut dengan tiga sifatnya. Kapasitasnya jauh lebih besar dan tumbuh mengikuti ruang penyimpanan perangkat. Isinya berupa object JavaScript utuh yang tidak perlu diurai manual. Setiap operasinya berjalan asinkron lewat event sehingga halaman tetap responsif. Aplikasi catatan luring, kumpulan artikel tersimpan, dan hasil kerja yang harus tetap ada tanpa jaringan adalah contoh pemakaiannya.
Database, object store, dan transaksi#
Susunan IndexedDB mengikuti istilah basis data pada umumnya. Satu database dipisahkan per origin dan dikenali lewat namanya. Di dalamnya terdapat satu atau lebih object store, yaitu wadah object sejenis yang mirip tabel dengan satu kunci untuk tiap isinya. Nama storenya bebas ditentukan sendiri, dan satu database yang sama boleh memuat banyak store untuk jenis data yang berbeda, misalnya catatan dan lampiran. Pencarian menurut kunci sudah cepat, sedangkan pencarian menurut bidang lain dipercepat oleh index, daftar bantu yang dibuat di atas satu bidang object store.
Semua pembacaan dan penulisan wajib berada di dalam transaksi, yaitu paket pekerjaan yang dijanjikan selesai utuh atau tidak sama sekali. Transaksi dibuat dengan menyebut object store yang akan disentuh beserta modenya, readonly untuk membaca dan readwrite untuk mengubah. Transaksi yang sudah selesai tidak dapat dipakai ulang, setiap kelompok operasi baru memerlukan transaksi baru, dan aturan ini justru menjaga data tetap konsisten. Di dalamnya, operasi menjadi permintaan (request) yang memancarkan event success atau error, dan pola event inilah yang membedakan IndexedDB dari API sinkron milik Web Storage.
Diagram berikut merangkum alurnya, dari pembukaan database sampai operasi pada object store yang berada di dalam transaksi.
Membuka database dan menulis data#
Pintu masuknya adalah indexedDB.open() yang menerima nama database dan nomor versinya. Panggilan ini memancarkan onupgradeneeded hanya ketika database belum ada atau versinya dinaikkan, dan di situlah object store dibuat dengan createObjectStore(). Setelah persiapan selesai, event onsuccess membawa database yang siap dipakai, sedangkan onerror menampung kegagalan pembukaannya.
// tidak-dijalankan
const permintaan = indexedDB.open("arsip-catatan", 1);
permintaan.onupgradeneeded = () => {
permintaan.result.createObjectStore("catatan");
};
permintaan.onsuccess = () => {
const db = permintaan.result; // database siap dipakai
};
permintaan.onerror = () => {
console.log("Database gagal dibuka");
};Menulis dan membaca mengikuti pola transaksi yang sudah dibahas. Satu transaksi dibuat untuk object store catatan dengan mode readwrite, permintaan put() menerima object beserta kuncinya, dan event oncomplete milik transaksi menandakan seluruh pekerjaannya tuntas. Kuncinya boleh diberikan manual seperti di bawah ini, atau dipercayakan ke store lewat opsi autoIncrement.
// tidak-dijalankan
const transaksi = db.transaction("catatan", "readwrite");
const store = transaksi.objectStore("catatan");
store.put({ teks: "Rapat kelas pukul 10", tanggal: "2026-11-02" }, 1);
transaksi.oncomplete = () => {
console.log("Catatan tersimpan");
};Demo berikut memperagakan pola lengkapnya pada satu object store sederhana. Tulis satu catatan, tekan Simpan catatan, dan isinya akan dimuat kembali dari IndexedDB setiap demo dibuka, termasuk setelah halaman dimuat ulang. Bila lingkungan yang dipakai memblokir IndexedDB, panel keterangan menampilkan pesan penggantinya dan halaman tetap berjalan.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Demo menyimpan satu catatan di IndexedDB</title>
<style>
body { font-family: "Segoe UI", Arial, sans-serif; margin: 14px; font-size: 14px; color: #1f2937; }
label { display: block; font-weight: 600; margin-bottom: 6px; }
input { padding: 6px 8px; width: 260px; max-width: 100%; }
button { padding: 6px 12px; margin: 10px 0; cursor: pointer; display: block; }
#isi { padding: 10px; background: #f1f5f9; border: 1px solid #94a3b8; border-radius: 8px; max-width: 360px; min-height: 20px; }
#info { margin-top: 10px; font-size: 13px; color: #64748b; max-width: 380px; }
</style>
</head>
<body>
<label for="teks">Catatan terakhir</label>
<input id="teks" type="text" placeholder="Tulis satu catatan" autocomplete="off">
<button id="simpan" type="button">Simpan catatan</button>
<div id="isi"></div>
<div id="info"></div>
<script src="index.js"></script>
<script>
// Demo IndexedDB: menyimpan satu catatan ke object store dan menampilkannya.
const info = document.querySelector("#info");
const isi = document.querySelector("#isi");
const teks = document.querySelector("#teks");
let db = null;
try {
const permintaan = indexedDB.open("demo-catatan", 1);
permintaan.onupgradeneeded = () => {
permintaan.result.createObjectStore("catatan");
};
permintaan.onsuccess = () => {
db = permintaan.result;
info.textContent = "Database siap dipakai.";
tampilkan();
};
permintaan.onerror = () => {
info.textContent = "Database gagal dibuka di lingkungan ini.";
};
} catch (error) {
info.textContent = "IndexedDB tidak tersedia di lingkungan ini.";
}
function simpan() {
if (!db) return;
const transaksi = db.transaction("catatan", "readwrite");
transaksi.objectStore("catatan").put(teks.value, "terakhir");
transaksi.oncomplete = tampilkan;
transaksi.onerror = () => {
info.textContent = "Penyimpanan gagal dijalankan.";
};
}
function tampilkan() {
if (!db) return;
const transaksi = db.transaction("catatan", "readonly");
const permintaan = transaksi.objectStore("catatan").get("terakhir");
permintaan.onsuccess = () => {
isi.textContent = permintaan.result === undefined
? "(belum ada catatan)"
: permintaan.result;
};
}
document.querySelector("#simpan").addEventListener("click", simpan);
</script>
<script>
// Demo IndexedDB: menyimpan satu catatan ke object store dan menampilkannya.
const info = document.querySelector("#info");
const isi = document.querySelector("#isi");
const teks = document.querySelector("#teks");
let db = null;
try {
const permintaan = indexedDB.open("demo-catatan", 1);
permintaan.onupgradeneeded = () => {
permintaan.result.createObjectStore("catatan");
};
permintaan.onsuccess = () => {
db = permintaan.result;
info.textContent = "Database siap dipakai.";
tampilkan();
};
permintaan.onerror = () => {
info.textContent = "Database gagal dibuka di lingkungan ini.";
};
} catch (error) {
info.textContent = "IndexedDB tidak tersedia di lingkungan ini.";
}
function simpan() {
if (!db) return;
const transaksi = db.transaction("catatan", "readwrite");
transaksi.objectStore("catatan").put(teks.value, "terakhir");
transaksi.oncomplete = tampilkan;
transaksi.onerror = () => {
info.textContent = "Penyimpanan gagal dijalankan.";
};
}
function tampilkan() {
if (!db) return;
const transaksi = db.transaction("catatan", "readonly");
const permintaan = transaksi.objectStore("catatan").get("terakhir");
permintaan.onsuccess = () => {
isi.textContent = permintaan.result === undefined
? "(belum ada catatan)"
: permintaan.result;
};
}
document.querySelector("#simpan").addEventListener("click", simpan);
</script></body>
</html>Kesalahan umum#
- Menulis operasi sebelum onsuccess. Database belum tersedia selama panggilan
openmasih berjalan, sehingga variabeldbmasihnulldan pembentukan transaksinya melempar error. Seluruh pekerjaan pertama harus menunggu di dalam handleronsuccess. - Lupa membuat object store pada versi yang baru.
createObjectStore()hanya sah dipanggil di dalamonupgradeneeded, dan store yang terlewat membuat transaksi berikutnya gagal karena nama storenya tidak dikenal. Naikkan nomor versi dan lengkapi penyiapannya di sana. - Menyela transaksi dengan jeda asinkron. Transaksi IndexedDB ditutup otomatis ketika tidak ada permintaan yang mengantre, sehingga jeda antar operasi membuat operasi berikutnya ditolak. Antrekan seluruh permintaan dalam satu giliran eksekusi yang sama.
Ringkasan#
- IndexedDB adalah basis data di dalam browser untuk object dalam jumlah besar, dengan kapasitas jauh melampaui Web Storage dan operasi asinkron berbasis event.
- Susunannya bertingkat, database per origin menampung object store berkunci, dan index mempercepat pencarian menurut bidang selain kunci.
- Setiap put dan get berada di dalam transaksi bermode
readonlyataureadwrite, lalu memancarkan eventsuccessatauerror. indexedDB.open()memicuonupgradeneededuntuk penyiapan object store, dan hasilnya dipakai di handleronsuccess.
Referensi#
- IndexedDB API — MDN — gambaran konsep database, object store, dan transaksi.
- Using IndexedDB — MDN — panduan langkah demi langkah membuka database dan menjalankan operasi.
- IDBObjectStore — MDN
— dokumentasi
put,get, dan pembuatan index pada object store.