Skip to content

Commit 72a61f4

Browse files
authored
Merge pull request #378 from odsantos/update-en-Indexeddb
Update "IndexedDB" article
2 parents 997e0c2 + 20506f9 commit 72a61f4

File tree

1 file changed

+19
-7
lines changed

1 file changed

+19
-7
lines changed

6-data-storage/03-indexeddb/article.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,12 @@ A key must be one of these types - number, date, string, binary, or array. It's
193193

194194
![](indexeddb-structure.svg)
195195

196-
197196
As we'll see very soon, we can provide a key when we add a value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows setting up an object property as the key, which is much more convenient. Or we can auto-generate keys.
198197

199198
But we need to create an object store first.
200199

201-
202200
The syntax to create an object store:
201+
203202
```js
204203
db.createObjectStore(name[, keyOptions]);
205204
```
@@ -214,6 +213,7 @@ Please note, the operation is synchronous, no `await` needed.
214213
If we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object.
215214

216215
For instance, this object store uses `id` property as the key:
216+
217217
```js
218218
db.createObjectStore('books', {keyPath: 'id'});
219219
```
@@ -223,6 +223,7 @@ db.createObjectStore('books', {keyPath: 'id'});
223223
That's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can only be created/removed/altered during a version update.
224224

225225
To perform a database version upgrade, there are two main approaches:
226+
226227
1. We can implement per-version upgrade functions: from 1 to 2, from 2 to 3, from 3 to 4 etc. Then, in `upgradeneeded` we can compare versions (e.g. old 2, now 4) and run per-version upgrades step by step, for every intermediate version (2 to 3, then 3 to 4).
227228
2. Or we can just examine the database: get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) that provides `contains(name)` method to check for existance. And then we can do updates depending on what exists and what doesn't.
228229

@@ -242,7 +243,6 @@ openRequest.onupgradeneeded = function() {
242243
};
243244
```
244245

245-
246246
To delete an object store:
247247

248248
```js
@@ -256,6 +256,7 @@ The term "transaction" is generic, used in many kinds of databases.
256256
A transaction is a group of operations, that should either all succeed or all fail.
257257

258258
For instance, when a person buys something, we need to:
259+
259260
1. Subtract the money from their account.
260261
2. Add the item to their inventory.
261262

@@ -276,7 +277,7 @@ db.transaction(store[, type]);
276277
- `readonly` -- can only read, the default.
277278
- `readwrite` -- can only read and write the data, but not create/remove/alter object stores.
278279

279-
There's also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `updateneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores.
280+
There's also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `upgradeneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores.
280281

281282
```smart header="Why are there different types of transactions?"
282283
Performance is the reason why transactions need to be labeled either `readonly` and `readwrite`.
@@ -614,6 +615,7 @@ The `delete` method looks up values to delete by a query, the call format is sim
614615
- **`delete(query)`** -- delete matching values by query.
615616

616617
For instance:
618+
617619
```js
618620
// delete the book with id='js'
619621
books.delete('js');
@@ -632,6 +634,7 @@ request.onsuccess = function() {
632634
```
633635

634636
To delete everything:
637+
635638
```js
636639
books.clear(); // clear the storage.
637640
```
@@ -651,6 +654,7 @@ Cursors provide the means to work around that.
651654
As an object store is sorted internally by key, a cursor walks the store in key order (ascending by default).
652655

653656
The syntax:
657+
654658
```js
655659
// like getAll, but with a cursor:
656660
let request = store.openCursor(query, [direction]);
@@ -748,7 +752,6 @@ try {
748752
} catch(err) {
749753
console.log('error', err.message);
750754
}
751-
752755
```
753756

754757
So we have all the sweet "plain async code" and "try..catch" stuff.
@@ -771,10 +774,8 @@ window.addEventListener('unhandledrejection', event => {
771774

772775
### "Inactive transaction" pitfall
773776

774-
775777
As we already know, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put a *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it would fail.
776778

777-
778779
For a promise wrapper and `async/await` the situation is the same.
779780

780781
Here's an example of `fetch` in the middle of the transaction:
@@ -793,6 +794,7 @@ await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error
793794
The next `inventory.add` after `fetch` `(*)` fails with an "inactive transaction" error, because the transaction is already committed and closed at that time.
794795

795796
The workaround is the same as when working with native IndexedDB: either make a new transaction or just split things apart.
797+
796798
1. Prepare the data and fetch all that's needed first.
797799
2. Then save in the database.
798800

@@ -826,3 +828,13 @@ The basic usage can be described with a few phrases:
826828
1. Get a promise wrapper like [idb](https://github.com/jakearchibald/idb).
827829
2. Open a database: `idb.openDb(name, version, onupgradeneeded)`
828830
- Create object storages and indexes in `onupgradeneeded` handler or perform version update if needed.
831+
3. For requests:
832+
- Create transaction `db.transaction('books')` (readwrite if needed).
833+
- Get the object store `transaction.objectStore('books')`.
834+
4. Then, to search by a key, call methods on the object store directly.
835+
- To search by an object field, create an index.
836+
5. If the data does not fit in memory, use a cursor.
837+
838+
Here's a small demo app:
839+
840+
[codetabs src="books" current="index.html"]

0 commit comments

Comments
 (0)