Cookies are usually limited to about 4KB, and LocalStorage typically ranges from roughly 2.5MB to 10MB. More importantly, LocalStorage does not support searching or custom indexes. That makes both of them poor choices for storing large amounts of data in the browser.
IndexedDB exists to solve that gap. It is a browser-provided local database and a transactional database system that can be created and manipulated through web scripts. Compared with LocalStorage, it is much better suited for large datasets. It supports lookups and indexes, which LocalStorage does not. It also differs from a traditional relational database: IndexedDB does not use SQL, and its model is closer to NoSQL.
Opening a database
An IndexedDB database can be opened directly. If the named database does not already exist, the browser creates it automatically.
The open method accepts two arguments: the database name and the database version. If no version is provided, the current version is used. If the database is created automatically because it does not exist yet, the default version is 1.
const request = indexedDB.open(databaseName, version)
The open call returns an IDBRequest instance.
If the request succeeds, its result can be accessed through the result property, and the request fires a success event.
If an error occurs, an error event is fired, and the result property throws an exception.
If the specified version is greater than the current version, the upgradeneeded event will fire. The same event also runs when the database is created for the first time.
const request = window.indexedDB.open('testDB')
request.onsuccess = (event) => {
const db = event.target.result
console.log('Database opened successfully!')
}
request.onerror = (event) => {
console.log('Database opened failed!')
}
request.onupgradeneeded = (event) => {
console.log('Database upgrade needed!')
}
Creating a store
Once a database is opened or created, the database object is available through result, and all later operations are performed on that object.
A store is created with createObjectStore, which is roughly comparable to a table.
// version 默认为 1
const request = indexedDB.open('testDB')
request.onupgradeneeded = (event) => {
const db = event.target.result
let objectStore
if (!db.objectStoreNames.contains('user')) {
objectStore = db.createObjectStore('user', { keyPath: 'id' })
}
}
This opens the database, checks whether a user store already exists, and creates it if it does not. The primary key is set to id.
The primary key is indexed by default. For example, if a record looks like { id: 1, name: '张三' }, the id field can serve as the key. A nested property can also be used as the key path. In an object like { foo: { bar: 'baz' } }, foo.bar can be used as the primary key.
If the data does not contain a suitable property for a key, IndexedDB can generate one automatically. In the example below, the key is an auto-incrementing integer.
objectStore = db.createObjectStore('user', { autoIncrement: true })
Creating indexes
Indexes can be added with createIndex.
request.onupgradeneeded = function (event) {
db = event.target.result
var objectStore = db.createObjectStore('user', { keyPath: 'id' })
objectStore.createIndex('name', 'name', { unique: false })
objectStore.createIndex('age', 'age', { unique: true })
}
Here, two indexes are created on the user store: one for name and one for age. The name index allows duplicate values, while the age index is marked as unique.
Working with data
Because IndexedDB is transactional, data operations are done inside transactions. This helps preserve consistency and integrity.
Add
Adding data starts with a transaction and access to the target object store.
const transaction = db.transaction('user', 'readwrite')
const objectStore = transaction.objectStore('user')
const data = { id: 1, name: 'Mengke', age: 26 }
const request = objectStore.add(data)
request.onsuccess = () => {
console.log('数据添加成功')
}
To write data, a transaction must be created first. When creating it, you have to specify the store name and the mode, such as read-only or read-write. After that, IDBTransaction.objectStore(name) returns the IDBObjectStore, and the store's add() method writes a record.
This write is asynchronous, so success and failure are handled through events such as success and error.
Delete
If a record needs to be removed, the deletion is also done through a transaction.
const transaction = db.transaction('user', 'readwrite')
const objectStore = transaction.objectStore('user')
const request = objectStore.delete(1)
request.onsuccess = () => {
console.log('数据删除成功')
}
Update
To modify existing data, retrieve the record first, change the fields you need, and then write it back with put().
const transaction = db.transaction('user', 'readwrite')
const objectStore = transaction.objectStore('user')
const request = objectStore.get(1)
request.onsuccess = (event) => {
const data = event.target.result
data.age = 27
const updateRequest = objectStore.put(data)
updateRequest.onsuccess = () => {
console.log('数据更新成功')
}
}
Read
Once records are stored, they can be retrieved by key.
const transaction = db.transaction('user', 'readonly')
const objectStore = transaction.objectStore('user')
const request = objectStore.get(1)
request.onsuccess = (event) => {
const data = event.target.result
console.log(data)
}
Iterate through records
IndexedDB also supports scanning data with a cursor. This is useful when you need to process records one by one.
const transaction = db.transaction('user', 'readonly')
const objectStore = transaction.objectStore('user')
const request = objectStore.openCursor()
request.onsuccess = (event) => {
const cursor = event.target.result
if (cursor) {
console.log(cursor.value)
cursor.continue()
}
}
Why indexes matter
Indexes in IndexedDB improve query performance. They let you sort and filter records based on specific fields, making retrieval faster than scanning everything manually.
const objectStore = db.createObjectStore('user', { keyPath: 'id' })
objectStore.createIndex('nameIndex', 'name', { unique: false })
That example creates an index named nameIndex on the name field. With indexes in place, IndexedDB becomes far more practical for applications that need more than simple key-value storage.