JavaScript の IndexedDB を使ってブラウザにデータを保存しよう

TwitterFacebookHatena

TL;DR

このページでは、JavaScript IndexedDB の魅力とその使い方を解説しますね。一言で言うと、IndexedDB は JavaScript で使える強力なデータベースです。

開発環境 バージョン
Next.js 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

IndexedDB とは?

皆さんはデータをどこに保存しますか?ローカルの変数?それともサーバー?どちらも有効な方法ですが、ブラウザ内にも強力なデータストレージが存在します。それが IndexedDB です。IndexedDB は JavaScript で利用できる Web 標準のデータベースで、オフラインでも安心して使えます。

IndexedDB の利用法は非常にシンプルです。まずデータベースを開きます。

const openRequest = indexedDB.open('myDatabase', 1)

このようにindexedDB.openメソッドを呼び出すだけで、新しいデータベースが開かれます。この際、第一引数にはデータベースの名前を、第二引数にはデータベースのバージョンを指定します。

第二引数

IndexedDB の open メソッドの第二引数で指定するのはデータベースのバージョン番号です。このバージョン番号は、整数で指定します。

let openRequest = indexedDB.open('myDatabase', 1)

上記のコードでは、データベース "myDatabase" のバージョン 1 を開くように指定しています。

バージョンはデータベースのスキーマ(構造)を管理するために使用されます。たとえば、新しいオブジェクトストアを作成したり、既存のオブジェクトストアにインデックスを追加したりする場合、データベースのバージョンを上げる必要があります。

新しいバージョンのデータベースを開くと、onupgradeneeded イベントが発火します。このイベント内では、データベースのスキーマを変更することができます。つまり、新しいオブジェクトストアを作成したり、インデックスを追加したりすることができます。

また、データベースのバージョンは一度設定すると下げることはできません。これは、データの整合性を保つための重要な機能です。

以下に、新しいバージョンのデータベースを開いて新しいオブジェクトストアを作成する例を示します。

let openRequest = indexedDB.open('myDatabase', 2) // バージョンを2に上げる

openRequest.onupgradeneeded = function () {
  let db = openRequest.result
  if (!db.objectStoreNames.contains('magazines')) {
    // "magazines"という名前のオブジェクトストアが存在しなければ作成
    db.createObjectStore('magazines', { keyPath: 'id' }) // キーとなるパスを指定
  }
}

ここでは、バージョンを 2 に上げて "magazines" という新しいオブジェクトストアを作成しています。

このように、バージョン管理を理解し利用することで、データベースのスキーマを効果的に管理することができます。

データの保持期間、消失条件

IndexedDB のデータベースは、ブラウザが管理します。ユーザーが明示的にブラウザのストレージをクリアしない限り、データは消えません。また、IndexedDB は Web ページがリロードされてもデータが保持されるため、セッション間でデータを保存するのに適しています。

ただし、以下のような状況でデータが消失する可能性があります。

  1. ユーザーが自身のブラウザの設定でデータをクリアする。
  2. ブラウザがディスクスペースを確保するためにデータを削除する。これは特にデバイスの空き容量が少ない場合に発生します。この挙動はブラウザによるものなので、どのデータが削除されるかは確定的ではありません。
  3. 一部のプライバシーフォーカスのブラウザは、閲覧履歴をクリアするための設定がある。これを使うと IndexedDB のデータも消える可能性があります。

したがって、IndexedDB は永続的なストレージとして使用できますが、データが消失しないという保証はありません。重要なデータはサーバー側にもバックアップを取るなど、適切なデータ管理を行うことが重要です。

データの作成と読み出し

それでは、実際にデータを IndexedDB に保存してみましょう。そのためには、まずオブジェクトストアという概念を理解する必要があります。オブジェクトストアはデータベース内の「棚」のようなもので、データの保存場所を定義します。

type DBEvent = Event & {
  target: IDBOpenDBRequest
}

const openRequest = indexedDB.open('myDatabase', 1)

openRequest.onupgradeneeded = (event: DBEvent) => {
  const db = event.target.result
  db.createObjectStore('myObjectStore')
}

このコードは新しいデータベースを開き、新しいオブジェクトストアを作成します。onupgradeneededイベントはデータベースが開かれた時、またはバージョンが上がった時に呼び出されます。ここでオブジェクトストアを作成することができます。

そしてデータを保存するには、オブジェクトストアのaddメソッドを使用します。

const openRequest = indexedDB.open('myDatabase', 1)

openRequest.onsuccess = (event: DBEvent) => {
  const db = event.target.result
  const transaction = db.transaction('myObjectStore', 'readwrite')
  const objectStore = transaction.objectStore('myObjectStore')
  objectStore.add({ hello: 'world' })
}

以上でデータの保存ができます。onsuccessイベントはデータベースが正常に開かれた時に発火します。そこでトランザクションを作成し、その中にオブジェクトストアを開きます。最後にaddメソッドでデータを追加します。

データの更新と削除

データの更新や削除も同様に簡単に行うことができます。更新にはputメソッドを、削除にはdeleteメソッドを使用します。

const openRequest = indexedDB.open('myDatabase', 1)

openRequest.onsuccess = (event: DBEvent) => {
  const db = event.target.result
  const transaction = db.transaction('myObjectStore', 'readwrite')
  const objectStore = transaction.objectStore('myObjectStore')

  // Update
  objectStore.put({ hello: 'new world' }, 'myKey')

  // Delete
  objectStore.delete('myKey')
}

このように IndexedDB を使うと、データの操作が非常に簡単になります。しかもこれらのデータはブラウザ内に保存されるため、サーバーとの通信がなくてもデータの操作が可能です。

オブジェクトストア

IndexedDB の中に存在する重要な考え方の一つが、オブジェクトストアです。IndexedDB は、JavaScript でブラウザ内のデータベースを操作するための API ですが、その中でデータを保存するための場所としてオブジェクトストアが存在します。

オブジェクトストアは、名前の通り、JavaScript のオブジェクトを保存する場所であり、それぞれのオブジェクトストアにはユニークな名前がつけられています。一つの IndexedDB データベース内には複数のオブジェクトストアを作成することができます。これは、リレーショナルデータベースのテーブルに似ていると考えると理解が深まるかもしれません。

また、オブジェクトストア内の各オブジェクトはキーによって一意に識別されます。このキーは、オブジェクトのプロパティの一つである場合もありますし、オブジェクトとは独立にデータベースが生成する場合もあります。

以下に、オブジェクトストアを作成し、データを保存する基本的なコードを示します。

// データベースを開く
let openRequest = indexedDB.open('myDatabase', 1)

openRequest.onupgradeneeded = function () {
  // データベースのバージョンが上がったとき、オブジェクトストアを作成
  let db = openRequest.result
  if (!db.objectStoreNames.contains('books')) {
    // "books"という名前のオブジェクトストアが存在しなければ作成
    db.createObjectStore('books', { keyPath: 'isbn' }) // キーとなるパスを指定
  }
}

openRequest.onsuccess = function () {
  let db = openRequest.result
  let transaction = db.transaction('books', 'readwrite') // "books"というオブジェクトストアに対してトランザクションを開始
  let books = transaction.objectStore('books') // オブジェクトストアを取得

  // データを追加
  let request = books.add({
    isbn: '123456',
    title: 'JavaScript入門',
    author: '田中 太郎',
  })

  request.onsuccess = function () {
    console.log('書籍が追加されました')
  }
}

ここでは、まず indexedDB.open を用いてデータベースを開いています。その後、onupgradeneeded イベント内で、"books"という名前のオブジェクトストアを作成しています。createObjectStore の第二引数にはオプションを指定し、ここでは keyPath を指定しています。この keyPath は、オブジェクト内の何をキーとするかを指定するオプションで、ここでは "isbn" をキーとしています。

そして、onsuccess イベント内で、オブジェクトストアに対するトランザクションを開始し、オブジェクトストアを取得しています。そして、add メソッドを使ってデータを追加しています。

このように、オブジェクトストアを理解し使いこなすことで、JavaScript でブラウザ内のデータベースを柔軟に操作することができます。

JavaScript の IndexedDB を使ってブラウザにデータを保存しよう