Data storage and retrieval operations with support for primary keys, indexes, and complex queries.
Primary storage interface for data records with key-based access and indexing support.
interface IDBObjectStore {
/** Object store name (can be changed during versionchange transaction) */
name: string;
/** Key path used for primary keys (readonly) */
readonly keyPath: string | string[] | null;
/** Whether keys are auto-generated (readonly) */
readonly autoIncrement: boolean;
/** List of index names in this store (readonly) */
readonly indexNames: DOMStringList;
/** Transaction this store belongs to (readonly) */
readonly transaction: IDBTransaction;
/**
* Stores a record, replacing if key exists
* @param value - Data to store
* @param key - Primary key (optional if keyPath or autoIncrement used)
* @returns Request completing with the stored key
*/
put(value: any, key?: IDBValidKey): IDBRequest;
/**
* Adds a record, failing if key exists
* @param value - Data to store
* @param key - Primary key (optional if keyPath or autoIncrement used)
* @returns Request completing with the stored key
*/
add(value: any, key?: IDBValidKey): IDBRequest;
/**
* Deletes records matching the key or key range
* @param key - Key or key range to delete
* @returns Request completing when deletion is done
*/
delete(key: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Retrieves a single record by key
* @param key - Key or key range to retrieve
* @returns Request completing with the record value
*/
get(key: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Retrieves multiple records
* @param query - Optional key or key range
* @param count - Maximum number of records to return
* @returns Request completing with array of values
*/
getAll(query?: IDBValidKey | IDBKeyRange, count?: number): IDBRequest;
/**
* Retrieves primary key without the full record
* @param key - Key or key range to find
* @returns Request completing with the primary key
*/
getKey(key: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Retrieves multiple primary keys
* @param query - Optional key or key range
* @param count - Maximum number of keys to return
* @returns Request completing with array of keys
*/
getAllKeys(query?: IDBValidKey | IDBKeyRange, count?: number): IDBRequest;
/**
* Counts records matching the key or key range
* @param key - Optional key or key range to count
* @returns Request completing with the count
*/
count(key?: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Removes all records from the store
* @returns Request completing when clearing is done
*/
clear(): IDBRequest;
/**
* Creates a new index (only during versionchange transaction)
* @param name - Index name
* @param keyPath - Property path(s) to index
* @param options - Index configuration
* @returns Created index
*/
createIndex(name: string, keyPath: string | string[], options?: {
unique?: boolean;
multiEntry?: boolean;
}): IDBIndex;
/**
* Gets an existing index
* @param name - Index name
* @returns The index object
*/
index(name: string): IDBIndex;
/**
* Deletes an index (only during versionchange transaction)
* @param name - Index name to delete
*/
deleteIndex(name: string): void;
/**
* Opens a cursor for iteration
* @param range - Optional key range to iterate
* @param direction - Cursor direction
* @returns Request completing with cursor or null
*/
openCursor(
range?: IDBValidKey | IDBKeyRange,
direction?: IDBCursorDirection
): IDBRequest;
/**
* Opens a key-only cursor for iteration
* @param range - Optional key range to iterate
* @param direction - Cursor direction
* @returns Request completing with cursor or null
*/
openKeyCursor(
range?: IDBValidKey | IDBKeyRange,
direction?: IDBCursorDirection
): IDBRequest;
}Usage Examples:
import "fake-indexeddb/auto";
// Open database and create transaction
const request = indexedDB.open("inventory", 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Create object store with compound key path
const productsStore = db.createObjectStore("products", {
keyPath: "sku" // Use product SKU as primary key
});
// Create object store with auto-increment
const ordersStore = db.createObjectStore("orders", {
keyPath: "id",
autoIncrement: true
});
// Create indexes for efficient querying
productsStore.createIndex("by_category", "category");
productsStore.createIndex("by_price", "price");
productsStore.createIndex("category_price", ["category", "price"]);
ordersStore.createIndex("by_customer", "customerId");
ordersStore.createIndex("by_date", "orderDate");
};
request.onsuccess = (event) => {
const db = event.target.result;
// Add data to store
const tx = db.transaction("products", "readwrite");
const store = tx.objectStore("products");
// Add records
store.add({
sku: "LAP001",
name: "Gaming Laptop",
category: "electronics",
price: 1299.99,
stock: 5
});
store.put({
sku: "LAP002",
name: "Business Laptop",
category: "electronics",
price: 899.99,
stock: 12
});
// Get single record
store.get("LAP001").onsuccess = (event) => {
const product = event.target.result;
console.log("Product:", product.name, "Price:", product.price);
};
// Get multiple records with limit
store.getAll(null, 10).onsuccess = (event) => {
const products = event.target.result;
console.log("First 10 products:", products);
};
// Count all records
store.count().onsuccess = (event) => {
console.log("Total products:", event.target.result);
};
tx.oncomplete = () => {
console.log("Transaction completed");
db.close();
};
};Secondary access path to object store data based on indexed properties.
interface IDBIndex {
/** Index name (can be changed during versionchange transaction) */
name: string;
/** Parent object store (readonly) */
readonly objectStore: IDBObjectStore;
/** Property path(s) this index covers (readonly) */
readonly keyPath: string | string[];
/** Whether index values must be unique (readonly) */
readonly unique: boolean;
/** Whether arrays are treated as multiple entries (readonly) */
readonly multiEntry: boolean;
/**
* Retrieves first record matching the index key
* @param key - Index key or key range to match
* @returns Request completing with the record value
*/
get(key: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Retrieves multiple records matching the index key
* @param query - Optional index key or key range
* @param count - Maximum number of records to return
* @returns Request completing with array of values
*/
getAll(query?: IDBValidKey | IDBKeyRange, count?: number): IDBRequest;
/**
* Retrieves primary key for first matching record
* @param key - Index key or key range to match
* @returns Request completing with the primary key
*/
getKey(key: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Retrieves multiple primary keys
* @param query - Optional index key or key range
* @param count - Maximum number of keys to return
* @returns Request completing with array of primary keys
*/
getAllKeys(query?: IDBValidKey | IDBKeyRange, count?: number): IDBRequest;
/**
* Counts records matching the index key
* @param key - Optional index key or key range to count
* @returns Request completing with the count
*/
count(key?: IDBValidKey | IDBKeyRange): IDBRequest;
/**
* Opens a cursor for iteration via index
* @param range - Optional key range to iterate
* @param direction - Cursor direction
* @returns Request completing with cursor or null
*/
openCursor(
range?: IDBValidKey | IDBKeyRange,
direction?: IDBCursorDirection
): IDBRequest;
/**
* Opens a key-only cursor for iteration via index
* @param range - Optional key range to iterate
* @param direction - Cursor direction
* @returns Request completing with cursor or null
*/
openKeyCursor(
range?: IDBValidKey | IDBKeyRange,
direction?: IDBCursorDirection
): IDBRequest;
}Usage Examples:
// Query by index
const tx = db.transaction("products", "readonly");
const store = tx.objectStore("products");
// Query by single index value
const categoryIndex = store.index("by_category");
categoryIndex.get("electronics").onsuccess = (event) => {
const product = event.target.result;
console.log("First electronics product:", product);
};
// Get all products in category
categoryIndex.getAll("electronics").onsuccess = (event) => {
const products = event.target.result;
console.log("All electronics:", products);
};
// Query by compound index (category + price)
const compoundIndex = store.index("category_price");
compoundIndex.getAll(["electronics", 1000]).onsuccess = (event) => {
const products = event.target.result;
console.log("Electronics costing $1000:", products);
};
// Count products in price range
const priceIndex = store.index("by_price");
const priceRange = IDBKeyRange.bound(500, 1500);
priceIndex.count(priceRange).onsuccess = (event) => {
console.log("Products in $500-$1500 range:", event.target.result);
};
// Iterate through index with cursor
priceIndex.openCursor().onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
const product = cursor.value;
console.log(`${product.name}: $${product.price}`);
cursor.continue();
}
};// Object store with auto-increment
const logsStore = db.createObjectStore("logs", {
keyPath: "id",
autoIncrement: true
});
// Records get automatically assigned sequential IDs
const tx = db.transaction("logs", "readwrite");
const store = tx.objectStore("logs");
store.add({
message: "User logged in",
timestamp: new Date(),
level: "info"
}); // Gets id: 1
store.add({
message: "Data processed",
timestamp: new Date(),
level: "debug"
}); // Gets id: 2// Object store without keyPath (out-of-line keys)
const cacheStore = db.createObjectStore("cache");
// Must provide keys explicitly
const tx = db.transaction("cache", "readwrite");
const store = tx.objectStore("cache");
store.put("cached data", "user:123");
store.put({ config: "settings" }, "app:config");
store.put([1, 2, 3], "numbers");// Create multi-entry index for arrays
const articlesStore = db.createObjectStore("articles", { keyPath: "id" });
articlesStore.createIndex("by_tags", "tags", { multiEntry: true });
// Add article with multiple tags
const tx = db.transaction("articles", "readwrite");
const store = tx.objectStore("articles");
store.add({
id: 1,
title: "IndexedDB Guide",
tags: ["javascript", "database", "tutorial"], // Array creates multiple index entries
content: "..."
});
// Query finds article by any tag
const tagIndex = store.index("by_tags");
tagIndex.get("javascript").onsuccess = (event) => {
// Finds the article above
console.log("Article about JavaScript:", event.target.result);
};// Efficient batch operations
const tx = db.transaction("products", "readwrite");
const store = tx.objectStore("products");
const products = [
{ sku: "A001", name: "Product A", price: 99.99 },
{ sku: "A002", name: "Product B", price: 149.99 },
{ sku: "A003", name: "Product C", price: 199.99 }
];
// Add all products in single transaction
products.forEach(product => {
store.add(product);
});
tx.oncomplete = () => {
console.log("All products added successfully");
};
tx.onerror = (event) => {
console.error("Batch operation failed:", event.target.error);
};// Validate data before storing
function addProduct(store, productData) {
// Validate required fields
if (!productData.sku || !productData.name || productData.price == null) {
throw new Error("Missing required product fields");
}
// Validate data types
if (typeof productData.price !== "number" || productData.price < 0) {
throw new Error("Invalid price");
}
// Sanitize data
const product = {
sku: productData.sku.toString().trim().toUpperCase(),
name: productData.name.toString().trim(),
price: Number(productData.price),
category: productData.category || "uncategorized",
createdAt: new Date()
};
return store.add(product);
}
// Usage
const tx = db.transaction("products", "readwrite");
const store = tx.objectStore("products");
try {
addProduct(store, {
sku: "lap001",
name: " Gaming Laptop ",
price: "1299.99",
category: "electronics"
});
} catch (error) {
console.error("Validation failed:", error.message);
}