LEARNING OBJECTIVES ⌵
- Differentiate between
add()(strict insert) andput()(upsert/replace). - Query records accurately using
get(),getAll(),count(), andgetKey(). - Remove records safely using
delete()and purge collections withclear(). - Understand the
IDBRequestlifecycle, event bubbling hierarchy, and error propagation.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine a physical post office box system with numbered lockboxes.
add(record)(Strict New Lease): You attempt to place a package into Box #42. If Box #42 already contains a package, the clerk firmly stops you: "Halt! Box #42 is occupied!" (AConstraintErroris raised).put(record)(Upsert / Overwrite): You tell the clerk: "Deliver this package to Box #42. If it's empty, put it in. If there's already something in there, replace it completely."get(key)(Fetch): You ask the clerk to look inside Box #42. If it contains an item, you receive it. If it is empty, you are handedundefined(not an error—just an empty box).delete(key)(Remove): You instruct the clerk to empty Box #42. If Box #42 was already empty, the operation still succeeds silently without error.- The
IDBRequestTicket: Every time you make a request at the counter, the clerk hands you a claim ticket (IDBRequest). You cannot peek at the contents immediately. When the clerk returns from the back room, your claim ticket lights up withonsuccessand you can readrequest.result.
Technical Deep Dive & Specifications
CRUD Methods on IDBObjectStore
+----------------------------------------------------------------------------------------------------+
| IDBOBJECTSTORE CRUD API MATRIX |
+-------------+-----------------------------+------------------------------------+-------------------+
| Method | Signature | Behavior | Return Value |
+-------------+-----------------------------+------------------------------------+-------------------+
| `add()` | `add(value, [key])` | Inserts record. Fails if key exists| Key of new record |
+-------------+-----------------------------+------------------------------------+-------------------+
| `put()` | `put(value, [key])` | Inserts or replaces existing record| Key of record |
+-------------+-----------------------------+------------------------------------+-------------------+
| `get()` | `get(key)` | Fetches record by primary key | Object or undef |
+-------------+-----------------------------+------------------------------------+-------------------+
| `getKey()` | `getKey(query)` | Fetches only the primary key | Key value |
+-------------+-----------------------------+------------------------------------+-------------------+
| `getAll()` | `getAll([query], [count])` | Fetches array of matching records | Array of objects |
+-------------+-----------------------------+------------------------------------+-------------------+
| `delete()` | `delete(key)` | Removes record by primary key | `undefined` |
+-------------+-----------------------------+------------------------------------+-------------------+
| `clear()` | `clear()` | Deletes ALL records in the store | `undefined` |
+-------------+-----------------------------+------------------------------------+-------------------+
| `count()` | `count([query])` | Counts number of matching records | Integer count |
+-------------+-----------------------------+------------------------------------+-------------------+
The IDBRequest Event & Bubbling Lifecycle
Whenever a CRUD operation is invoked, it synchronously returns an IDBRequest object in the "pending" state. When the underlying disk I/O completes, the browser fires events that bubble up through three tiers:
+-----------------------------+
| IDBRequest (Operation) |
| [onsuccess / onerror] |
+-----------------------------+
|
v (Bubbles on error)
+-----------------------------+
| IDBTransaction (Scope) |
| [oncomplete / onerror] |
+-----------------------------+
|
v (Bubbles on error)
+-----------------------------+
| IDBDatabase (Engine) |
| [onerror] |
+-----------------------------+
Crucial Rule:
onsuccessevents do not bubble. You must attachonsuccessdirectly to theIDBRequest. However,onerrorevents do bubble up to the parentIDBTransactionandIDBDatabaseunlessevent.stopPropagation()orevent.preventDefault()is called.
💻 Interactive Code Playground
Starter Code
Save this file as crud.html and open it in your browser.
Line-by-Line Code Breakdown
- Line 72 (
store.add(data)): Executes an insertion. If a record withid: "u101"already exists, this triggersrequest.onerrorwith aConstraintError. - Line 87 (
store.put(data)): Executes an upsert. Ifid: "u101"exists, it is cleanly overwritten with the new object; if it does not exist, it is inserted. - Lines 100–108 (
store.get(id)): Fetches the record. Notice that a missing record is not an error;request.resultis simplyundefined. - Line 115 (
store.getAll()): Fetches an array containing every record currently stored in the ObjectStore. - Line 129 (
store.delete(id)): Deletes the specified key. Even if the key does not exist,delete()succeeds silently. - Line 139 (
store.clear()): Truncates the entire ObjectStore, removing all records while preserving the store itself and its index definitions.
Expected Browser Render Output
[02:45:00] 🛠️ Created ObjectStore: "users" with keyPath "id"
[02:45:00] ✅ Database connected and ready.
[02:45:05] ➕ ADD Success: Inserted key "u101"
[02:45:10] ❌ ADD Failed: ConstraintError - Key already exists in the object store.
[02:45:15] 🔄 PUT Success: Upserted key "u101"
[02:45:20] 🔍 GET Result: {"id":"u101","name":"Sarah Connor","role":"Admin","updatedAt":"2026-08-21T02:45:15.100Z"}🏋️ Hands-On Exercise
🎯 The Challenge: Build a Resilient Shopping Cart Store
Instructions:
- Open database
ECommerceAppat Version 1 with storecart_items(keyPath: "sku"). - Create an
addToCart(item)function that checks if an item already exists:- If it does not exist, insert it with
quantity: 1. - If it already exists, increment its
quantityby 1 and update it usingput().
- If it does not exist, insert it with
- Test by adding an item
{ sku: "AIR-JORDAN-1", name: "Nike Air Jordan 1", price: 180 }three times and verify the finalquantityis3.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Expecting
get()on a Missing Key to Triggeronerror: If a key does not exist,store.get("missing_key")succeeds normally and setsrequest.resulttoundefined. Always testif (req.result !== undefined)inonsuccess. - Using
add()whenput()is Intended: Usingadd()in forms or synchronization routines will cause unexpectedConstraintErrorexceptions when records are re-submitted. - Uncontrolled
getAll()Memory Spikes: Callingstore.getAll()on an ObjectStore containing tens of thousands of large objects loads the entire dataset into JavaScript heap memory at once, potentially causing tab crashes. Use Cursors (Lesson 49.7) for large datasets.
💡 Pro Tips
- Use
count()for Existence Checks: If you only need to know whether a record exists or count matching items, callstore.count(key). This checks the index B-tree without deserializing the underlying object payload into JavaScript memory. - Leverage
getKey()to Avoid Payload Cloning: If you only need the primary key of a query match,store.getKey(query)avoids the Structured Clone overhead of the entire value object.
📌 Key Takeaways
add()is an insert-only operation that fails withConstraintErrorif the primary key already exists.put()is an upsert operation that inserts new records or overwrites existing ones.get()returns the matching record orundefined(it does not fail if the key is missing).delete()removes a record by primary key silently, even if the key is not present.getAll()retrieves an array of all records; use with caution on large datasets.onsuccessevents do not bubble, whileonerrorevents bubble to the parentIDBTransactionandIDBDatabase.- --