1 | # IDB-Keyval
|
2 |
|
3 | [![npm](https://img.shields.io/npm/v/idb-keyval.svg)](https://www.npmjs.com/package/idb-keyval)
|
4 |
|
5 | This is a super-simple promise-based keyval store implemented with IndexedDB, originally based on [async-storage by Mozilla](https://github.com/mozilla-b2g/gaia/blob/master/shared/js/async_storage.js).
|
6 |
|
7 | It's small and tree-shakeable. If you only use get/set, the library is ~370 bytes (brotli'd), if you use all methods it's ~570 bytes.
|
8 |
|
9 | Although this is tiny, it's a little larger than previous versions due to a [massive bug in Safari](https://bugs.webkit.org/show_bug.cgi?id=226547). Hopefully this fix can be removed in the not-too-distant future, when a version of Safari without the bug reaches enough users.
|
10 |
|
11 | [localForage](https://github.com/localForage/localForage) offers similar functionality, but supports older browsers with broken/absent IDB implementations. Because of that, it's orders of magnitude bigger (~7k).
|
12 |
|
13 | This is only a keyval store. If you need to do more complex things like iteration & indexing, check out [IDB on NPM](https://www.npmjs.com/package/idb) (a little heavier at 1k). The first example in its README is how to create a keyval store.
|
14 |
|
15 | ## Installing
|
16 |
|
17 | ### Recommended: Via npm + webpack/rollup/parcel/etc
|
18 |
|
19 | ```sh
|
20 | npm install idb-keyval
|
21 | ```
|
22 |
|
23 | Now you can require/import `idb-keyval`:
|
24 |
|
25 | ```js
|
26 | import { get, set } from 'idb-keyval';
|
27 | ```
|
28 |
|
29 | If you're targeting IE10/11, use the compat version, and import a `Promise` polyfill.
|
30 |
|
31 | ```js
|
32 | // Import a Promise polyfill
|
33 | import 'es6-promise/auto';
|
34 | import { get, set } from 'idb-keyval/dist/esm-compat';
|
35 | ```
|
36 |
|
37 | ### All bundles
|
38 |
|
39 | A well-behaved bundler should automatically pick the ES module or the CJS module depending on what it supports, but if you need to force it either way:
|
40 |
|
41 | - `idb-keyval/dist/index.js` EcmaScript module.
|
42 | - `idb-keyval/dist/index.cjs` CommonJS module.
|
43 |
|
44 | Legacy builds:
|
45 |
|
46 | - `idb-keyval/dist/compat.js` EcmaScript module, transpiled for older browsers.
|
47 | - `idb-keyval/dist/compat.cjs` CommonJS module, transpiled for older browsers.
|
48 | - `idb-keyval/dist/umd.js` UMD module, also transpiled for older browsers.
|
49 |
|
50 | These built versions are also available on jsDelivr, e.g.:
|
51 |
|
52 | ```html
|
53 | <script src="https://cdn.jsdelivr.net/npm/idb-keyval@6/dist/umd.js"></script>
|
54 | <!-- Or in modern browsers: -->
|
55 | <script type="module">
|
56 | import { get, set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm';
|
57 | </script>
|
58 | ```
|
59 |
|
60 | ## Usage
|
61 |
|
62 | ### set:
|
63 |
|
64 | ```js
|
65 | import { set } from 'idb-keyval';
|
66 |
|
67 | set('hello', 'world');
|
68 | ```
|
69 |
|
70 | Since this is IDB-backed, you can store anything structured-clonable (numbers, arrays, objects, dates, blobs etc), although old Edge doesn't support `null`. Keys can be numbers, strings, `Date`s, (IDB also allows arrays of those values, but IE doesn't support it).
|
71 |
|
72 | All methods return promises:
|
73 |
|
74 | ```js
|
75 | import { set } from 'idb-keyval';
|
76 |
|
77 | set('hello', 'world')
|
78 | .then(() => console.log('It worked!'))
|
79 | .catch((err) => console.log('It failed!', err));
|
80 | ```
|
81 |
|
82 | ### get:
|
83 |
|
84 | ```js
|
85 | import { get } from 'idb-keyval';
|
86 |
|
87 | // logs: "world"
|
88 | get('hello').then((val) => console.log(val));
|
89 | ```
|
90 |
|
91 | If there is no 'hello' key, then `val` will be `undefined`.
|
92 |
|
93 | ### setMany:
|
94 |
|
95 | Set many keyval pairs at once. This is faster than calling `set` multiple times.
|
96 |
|
97 | ```js
|
98 | import { set, setMany } from 'idb-keyval';
|
99 |
|
100 | // Instead of:
|
101 | Promise.all([set(123, 456), set('hello', 'world')])
|
102 | .then(() => console.log('It worked!'))
|
103 | .catch((err) => console.log('It failed!', err));
|
104 |
|
105 | // It's faster to do:
|
106 | setMany([
|
107 | [123, 456],
|
108 | ['hello', 'world'],
|
109 | ])
|
110 | .then(() => console.log('It worked!'))
|
111 | .catch((err) => console.log('It failed!', err));
|
112 | ```
|
113 |
|
114 | This operation is also atomic – if one of the pairs can't be added, none will be added.
|
115 |
|
116 | ### getMany:
|
117 |
|
118 | Get many keys at once. This is faster than calling `get` multiple times. Resolves with an array of values.
|
119 |
|
120 | ```js
|
121 | import { get, getMany } from 'idb-keyval';
|
122 |
|
123 | // Instead of:
|
124 | Promise.all([get(123), get('hello')]).then(([firstVal, secondVal]) =>
|
125 | console.log(firstVal, secondVal),
|
126 | );
|
127 |
|
128 | // It's faster to do:
|
129 | getMany([123, 'hello']).then(([firstVal, secondVal]) =>
|
130 | console.log(firstVal, secondVal),
|
131 | );
|
132 | ```
|
133 |
|
134 | ### update:
|
135 |
|
136 | Transforming a value (eg incrementing a number) using `get` and `set` is risky, as both `get` and `set` are async and non-atomic:
|
137 |
|
138 | ```js
|
139 | // Don't do this:
|
140 | import { get, set } from 'idb-keyval';
|
141 |
|
142 | get('counter').then((val) =>
|
143 | set('counter', (val || 0) + 1);
|
144 | );
|
145 |
|
146 | get('counter').then((val) =>
|
147 | set('counter', (val || 0) + 1);
|
148 | );
|
149 | ```
|
150 |
|
151 | With the above, both `get` operations will complete first, each returning `undefined`, then each set operation will be setting `1`. You could fix the above by queuing the second `get` on the first `set`, but that isn't always feasible across multiple pieces of code. Instead:
|
152 |
|
153 | ```js
|
154 | // Instead:
|
155 | import { update } from 'idb-keyval';
|
156 |
|
157 | update('counter', (val) => (val || 0) + 1);
|
158 | update('counter', (val) => (val || 0) + 1);
|
159 | ```
|
160 |
|
161 | This will queue the updates automatically, so the first `update` set the `counter` to `1`, and the second `update` sets it to `2`.
|
162 |
|
163 | ### del:
|
164 |
|
165 | Delete a particular key from the store.
|
166 |
|
167 | ```js
|
168 | import { del } from 'idb-keyval';
|
169 |
|
170 | del('hello');
|
171 | ```
|
172 |
|
173 | ### delMany:
|
174 |
|
175 | Delete many keys at once. This is faster than calling `del` multiple times.
|
176 |
|
177 | ```js
|
178 | import { del, delMany } from 'idb-keyval';
|
179 |
|
180 | // Instead of:
|
181 | Promise.all([del(123), del('hello')])
|
182 | .then(() => console.log('It worked!'))
|
183 | .catch((err) => console.log('It failed!', err));
|
184 |
|
185 | // It's faster to do:
|
186 | delMany([123, 'hello'])
|
187 | .then(() => console.log('It worked!'))
|
188 | .catch((err) => console.log('It failed!', err));
|
189 | ```
|
190 |
|
191 | ### clear:
|
192 |
|
193 | Clear all values in the store.
|
194 |
|
195 | ```js
|
196 | import { clear } from 'idb-keyval';
|
197 |
|
198 | clear();
|
199 | ```
|
200 |
|
201 | ### entries:
|
202 |
|
203 | Get all entries in the store. Each entry is an array of `[key, value]`.
|
204 |
|
205 | ```js
|
206 | import { entries } from 'idb-keyval';
|
207 |
|
208 | // logs: [[123, 456], ['hello', 'world']]
|
209 | entries().then((entries) => console.log(entries));
|
210 | ```
|
211 |
|
212 | ### keys:
|
213 |
|
214 | Get all keys in the store.
|
215 |
|
216 | ```js
|
217 | import { keys } from 'idb-keyval';
|
218 |
|
219 | // logs: [123, 'hello']
|
220 | keys().then((keys) => console.log(keys));
|
221 | ```
|
222 |
|
223 | ### values:
|
224 |
|
225 | Get all values in the store.
|
226 |
|
227 | ```js
|
228 | import { values } from 'idb-keyval';
|
229 |
|
230 | // logs: [456, 'world']
|
231 | values().then((values) => console.log(values));
|
232 | ```
|
233 |
|
234 | ### Custom stores:
|
235 |
|
236 | By default, the methods above use an IndexedDB database named `keyval-store` and an object store named `keyval`. If you want to use something different, see [custom stores](./custom-stores.md).
|