IndexedDB - Object Stores
IndexedDB: Object Stores
Section titled “IndexedDB: Object Stores”Object Stores are the core data containers in an IndexedDB database, analogous to ‘tables’ in a traditional SQL database. A single database can contain multiple object stores, each designed to hold a specific type of JavaScript object. This chapter details how to create, define keys for, and manage object stores.
Creating an Object Store
Section titled “Creating an Object Store”Object stores can only be created or modified within the onupgradeneeded event handler. This design ensures that all schema modifications happen in a controlled, versioned manner. You use the IDBDatabase.createObjectStore() method to create a new store.
db.createObjectStore(storeName, options);storeName: A string that names the object store.options: An optional object to configure the primary key. The two main properties arekeyPathandautoIncrement.
Defining Primary Keys
Section titled “Defining Primary Keys”Every record in an object store must have a unique key to identify it. IndexedDB offers two main ways to manage these primary keys:
- In-line Keys (Key Path): The key is a property of the object being stored. You specify this property using the
keyPathoption. For example, if you store user objects, you might use anemailoruserIdproperty as the key path:{ keyPath: 'userId' }. - Out-of-line Keys (Key Generator): The key is generated separately and stored alongside the value. By setting the
autoIncrementoption totrue, you can have IndexedDB automatically generate an increasing integer key for each new object. This is useful when your objects don’t have a naturally unique property.
You can use both keyPath and autoIncrement together. This is useful for assigning a unique, auto-incrementing ID to a specific property of your objects.
Example: Creating an Object Store with a Key Path
Section titled “Example: Creating an Object Store with a Key Path”const request = indexedDB.open("CustomerDB", 1);
request.onupgradeneeded = (event) => { const db = event.target.result;
// Create a 'customers' store where the 'email' property of each object is the primary key. const store = db.createObjectStore("customers", { keyPath: "email" });
console.log("'customers' object store created successfully.");};Example: Creating an Object Store with an Auto-Incrementing Key
Section titled “Example: Creating an Object Store with an Auto-Incrementing Key”const request = indexedDB.open("TaskDB", 1);
request.onupgradeneeded = (event) => { const db = event.target.result;
// Create a 'tasks' store with an auto-incrementing integer key. // The key will be stored separately from the task object. const store = db.createObjectStore("tasks", { autoIncrement: true });
// We can also combine them. // This will create a 'logs' store where each log object gets an 'id' property // that is automatically assigned an incrementing number. const logStore = db.createObjectStore("logs", { keyPath: 'id', autoIncrement: true });
console.log("'tasks' and 'logs' object stores created.");};Defining Indexes for Efficient Queries
Section titled “Defining Indexes for Efficient Queries”While a primary key allows you to retrieve a single record efficiently, indexes enable fast lookups based on other properties of your objects. An index is like a separate, sorted view of your data. You create indexes on an object store instance using the createIndex() method, also within onupgradeneeded.
objectStore.createIndex(indexName, keyPath, options);indexName: A name for the index.keyPath: The property of the stored object that the index should use for sorting and lookups. This can also be an array for compound indexes.options: An optional object with configuration properties, such asunique(ensures all values in the index are unique) andmultiEntry(for indexing array values).
Example: Creating Indexes and Seeding Data
Section titled “Example: Creating Indexes and Seeding Data”This example sets up a products store with indexes on category and price. It also shows how to add initial data right after the schema is created.
const request = indexedDB.open("StoreDB", 1);
request.onupgradeneeded = (event) => { const db = event.target.result;
// Create the object store const store = db.createObjectStore("products", { keyPath: "id" });
// Create an index on the 'category' property store.createIndex("by_category", "category", { unique: false });
// Create an index on the 'price' property store.createIndex("by_price", "price", { unique: false });
console.log('Object store and indexes are ready.');
// The transaction for onupgradeneeded is still active, // so we can add initial data here. const initialProducts = [ { id: 'p1', name: 'Modern JavaScript', category: 'books', price: 29.99 }, { id: 'p2', name: 'Web Development', category: 'books', price: 39.99 }, { id: 'p3', name: 'Mechanical Keyboard', category: 'electronics', price: 150.00 } ];
// To add data, we need to access the store again via the transaction const productStore = event.target.transaction.objectStore('products'); initialProducts.forEach(product => { productStore.add(product); }); console.log('Initial products seeded.');};Deleting an Object Store
Section titled “Deleting an Object Store”If an object store is no longer needed, you can delete it using db.deleteObjectStore(). Like creation, this must also be done inside an onupgradeneeded event, which means you must open the database with a new version number.
Example: Deleting an Object Store
Section titled “Example: Deleting an Object Store”// Let's assume 'StoreDB' is at version 1 and has an 'obsoleteStore'.// To delete it, we open the DB with a higher version number.const request = indexedDB.open("StoreDB", 2);
request.onupgradeneeded = (event) => { const db = event.target.result;
// Check if the store exists before trying to delete it if (db.objectStoreNames.contains('obsoleteStore')) { db.deleteObjectStore("obsoleteStore"); console.log("'obsoleteStore' has been deleted."); }};