1分でわかるJavaScriptのIndexedDBの使い方

IndexedDBとは

IndexedDBは、ブラウザ内で利用可能なキーバリューストア型の非同期データベースです。

JavaScriptで大容量のデータを保存・管理でき、リッチなWebアプリケーションを構築する際に使用されます。

Web Storage API (sessionStorage or localStorage) は各ブラウザ対応を考慮した場合、最大5MBしか保存できませんが、IndexedDBなら1GBは保存できるので、データ量が多い場合は必然的にIndexedDBを使用します。

IndexedDBの使い方

最初にアクセスするデータベースの名前を指定して、データベース内の「オブジェクトストア」の名前を指定します。

次に指定した名前のデータベースに接続するためのリクエストを作成します。

request.onsuccessでデータベース名などが取得できていれば接続成功です。

JavaScript
const DB_NAME = 'UserDatabase'
const STORE_NAME = 'users'
const request = indexedDB.open(DB_NAME)

request.onsuccess = (event) => {
  const db = event.target.result
  console.log(db.name)
  // UserDatabase
}

データベースを取得および追加したいコードは以下のようになります。

初回のみrequest.onupgradeneededでオブジェクトストアを作成します。

db.transaction() でデータベース内で操作を行うためのトランザクションを作成して、transaction.objectStore()でトランザクション内で操作する対象のオブジェクトストア(データが格納されている場所)を取得します。(今回の例ではusers)

あとはstore.get() で取得、store.add()で追加ができます。

今回のサンプルには追加していませんが、store.put() で更新、store.delete() で削除ができます。

詳細はMdNのIDBObjectStoreのインスタンスメソッドを参照してください。

JavaScript
const DB_NAME = 'UserDatabase'
const STORE_NAME = 'users'
const request = indexedDB.open(DB_NAME)

request.onsuccess = (event) => {
  const db = event.target.result
  const transaction = db.transaction(STORE_NAME, 'readonly')
  const store = transaction.objectStore(STORE_NAME)
  const countRequest = store.count()

  countRequest.onsuccess = () => {
    const userName = document.getElementById('userName')
    const addUserBtn = document.getElementById('addUserBtn')
    const userId = document.getElementById('userId')
    const userIdBtn = document.getElementById('userIdBtn')
    const result = document.getElementById('result')
    let id = countRequest.result

    addUserBtn.addEventListener('click', () => {
      if (!userName.value) return
      addUser(db, { id: ++id, name: userName.value })
      userName.value = ''
    })

    userIdBtn.addEventListener('click', () => {
      if (!userId.value) return
      getUser(db, parseInt(userId.value))
    })
  }
}

request.onupgradeneeded = (event) => {
  const db = event.target.result

  if (!db.objectStoreNames.contains(STORE_NAME)) {
    const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'id' })
    objectStore.createIndex('name', 'name', { unique: false })
    objectStore.createIndex('age', 'age', { unique: false })
    console.log('オブジェクトストアが作成されました')
  }
}

function addUser(db, user) {
  const transaction = db.transaction(STORE_NAME, 'readwrite')
  const store = transaction.objectStore(STORE_NAME)
  const getRequest = store.get(user.id)

  getRequest.onsuccess = () => {
    if (getRequest.result) {
      console.log('同じIDのユーザーがすでに存在します:', getRequest.result)
    } else {
      const addRequest = store.add(user)

      addRequest.onsuccess = () => {
        console.log('ユーザーが追加されました:', user)
      }
    }
  }
}

function getUser(db, id) {
  const transaction = db.transaction(STORE_NAME, 'readonly')
  const store = transaction.objectStore(STORE_NAME)
  const request = store.get(id)
  const result = document.getElementById('result')

  request.onsuccess = () => {
    result.textContent = request.result?.name ?? `id: ${id} のユーザーは存在しません`
  }
}

IndexedDBのget(), add(), count()のサンプル