Skip to content

Creating and Querying Indexes

Every IndexedDB object store is keyed by its primary key (the keyPath or an auto-incremented id). That is efficient for lookups by primary key, but if you want to query by a different field — for example, find all contacts by their name — you need an index.

An index is a secondary sorted structure that maps a chosen field’s value back to the record’s primary key. Once created, you can call get() or getAll() on the index exactly as you would on the store itself.

Indexes must be created (or deleted) inside the onupgradeneeded callback. You cannot add an index to an existing store outside of an upgrade transaction.

const request = indexedDB.open('mydb', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Create the object store
const store = db.createObjectStore('contacts', { keyPath: 'id' });
// Create an index on the 'name' field
store.createIndex('by_name', 'name', { unique: false });
};

createIndex(name, keyPath, options) parameters

Section titled “createIndex(name, keyPath, options) parameters”
ParameterPurpose
nameThe index name you will use to retrieve it later with store.index(name)
keyPathThe property path on each record to index (e.g. 'name', 'address.city')
uniqueIf true, the store rejects records whose indexed value duplicates an existing one
multiEntryIf true and the indexed property is an array, each array element gets its own entry in the index

After the upgrade, open the index from a normal read/write transaction with store.index('by_name'), then call get(value) for a single record or getAll(value) for all matching records.

const tx = db.transaction('contacts', 'readonly');
const store = tx.objectStore('contacts');
const nameIndex = store.index('by_name');
// Single match
const getReq = nameIndex.get('Alice');
getReq.onsuccess = () => console.log(getReq.result); // { id: 1, name: 'Alice', ... }
// All records (no filter → returns every record sorted by index key)
const getAllReq = nameIndex.getAll();
getAllReq.onsuccess = () => console.log(getAllReq.result);

Passing a value to getAll(value) filters to records whose indexed field equals that value. Passing no argument (or undefined) returns every record in the store, ordered by the index key.

// Enforce unique emails
store.createIndex('by_email', 'email', { unique: true });

With unique: true, any add() or put() that would create a duplicate indexed value throws a ConstraintError. Use this for fields like email addresses or usernames where duplicates are not allowed.

// Record: { id: 1, tags: ['js', 'idb', 'storage'] }
store.createIndex('by_tag', 'tags', { multiEntry: true });

With multiEntry: true, an array-valued field produces one index entry per element. You can then call index.getAll('idb') and the record above will be returned even though the value of tags is an array.

Browser Storage
OptionBenefitCost
Creating an index on a frequently-queried fieldMuch faster reads/searches on that field, no need to scan every recordEvery write must also update the index, slowing writes down and using extra storage
No index at all (search via cursor + your own filter)No write overhead, no extra storage usedReads/searches must scan every record, getting slower as the store grows
multiEntry: true on an array fieldEfficiently searches each element inside an arrayAdds one index entry per array element, so the index grows with the element count
  • Trying to create a new index outside onupgradeneeded — this throws immediately. Indexes can only be created or removed inside an upgrade transaction.
  • Setting unique: true on a field that already has duplicate values — writing a record with a duplicate indexed value throws ConstraintError immediately. Make sure the field is genuinely unique before turning this on.
  • Forgetting that getAll() on an index sorts by the index key, not the primary key — if you expect primary-key order but query through an index, the results are instead sorted by the indexed field.
Where must you call objectStore.createIndex() to successfully add an index?
What does setting { unique: true } on an index do?
You call nameIndex.getAll() with no arguments. What is returned?
A record has the field tags: ["js", "idb"]. Which createIndex option makes both "js" and "idb" individually searchable via index.getAll("js")?