Using IndexedDB Cursors and Key Ranges in JavaScript
This article provides an overview of IndexedDB cursors and explains
how to iterate through records using filtered key ranges in JavaScript.
You will learn the fundamental mechanisms of IDBCursor and
IDBKeyRange, how to construct precise boundary conditions,
and how to write efficient traversal logic to query client-side
databases without loading entire datasets into memory.
Understanding IndexedDB Cursors
An IndexedDB cursor (IDBCursor) is a pointer that
enables sequential traversal over multiple records in an object store or
index. Instead of fetching all matching records into memory at once with
methods like getAll(), a cursor loads records individually
on demand. This makes cursors memory-efficient and ideal for working
with large datasets, generating reports, or modifying records during
traversal.
When iterating with cursors, you typically use
IDBCursorWithValue, which gives access to both the primary
key (cursor.key), the primary index key
(cursor.primaryKey), and the stored object itself
(cursor.value).
Defining Filtered Key Ranges with IDBKeyRange
The IDBKeyRange interface defines criteria to limit
which records a cursor visits. Instead of scanning the entire store, a
key range restricts traversal to keys that satisfy specific boundary
conditions.
The IDBKeyRange utility provides four static factory
methods:
IDBKeyRange.only(value): Matches a single exact key value.IDBKeyRange.lowerBound(lower, [isOpen]): Matches all keys greater than (or equal to, ifisOpenisfalse) the specified lower limit.IDBKeyRange.upperBound(upper, [isOpen]): Matches all keys less than (or equal to, ifisOpenisfalse) the specified upper limit.IDBKeyRange.bound(lower, upper, [isLowerOpen], [isUpperOpen]): Matches all keys between two bounds. The optional boolean parameters specify whether to exclude the boundary values (open intervals).
// Matches ages between 21 and 65, inclusive
const standardRange = IDBKeyRange.bound(21, 65);
// Matches ages strictly greater than 21 and strictly less than 65 (exclusive)
const exclusiveRange = IDBKeyRange.bound(21, 65, true, true);
// Matches all prices 100 or greater
const minimumPrice = IDBKeyRange.lowerBound(100);How JavaScript Iterates Through a Key Range
To iterate through records using a cursor and key range, follow these steps:
- Start a Transaction: Create a
readonlyorreadwritetransaction on the target object store. - Access the Store or Index: Obtain a reference to the object store or an index configured on the field you want to filter.
- Open the Cursor: Call
openCursor(keyRange, direction)on the store or index. - Handle the
onsuccessEvent: Process the current record viaevent.target.result. - Advance the Cursor: Call
cursor.continue()to move to the next matching item.
Implementation Example
function iterateFilteredRecords(db) {
const transaction = db.transaction(['users'], 'readonly');
const store = transaction.objectStore('users');
const index = store.index('ageIndex');
// Define a range: ages 18 to 30 inclusive
const ageRange = IDBKeyRange.bound(18, 30);
// Open cursor passing the range and optional direction ('next', 'prev', 'nextunique', 'prevunique')
const request = index.openCursor(ageRange, 'next');
request.onsuccess = function (event) {
const cursor = event.target.result;
if (cursor) {
// Access the current matching record
console.log(`Key: ${cursor.key}, Value:`, cursor.value);
// Advance to the next matching record
cursor.continue();
} else {
// Reached the end of the matching range
console.log('Iteration complete.');
}
};
request.onerror = function (event) {
console.error('Cursor iteration error:', event.target.error);
};
}Controlling Iteration Flow
Cursors provide additional methods to control how the iteration progresses:
cursor.continue(key): Advances the cursor to the next record, or directly to the specified key if provided.cursor.advance(count): Skips a specified number of records forward in a single operation.cursor.update(newValue): Updates the record at the current cursor position (requires areadwritetransaction).cursor.delete(): Removes the record at the current cursor position from the store.
By pairing IDBCursor with IDBKeyRange,
JavaScript applications can query and manipulate indexed client-side
data with precision, minimal memory overhead, and full directional
control.