1 | <h1 align="center">
|
2 | <img width="250" src="https://jaredwray.com/images/keyv.svg" alt="keyv">
|
3 | <br>
|
4 | <br>
|
5 | </h1>
|
6 |
|
7 | > Simple key-value storage with support for multiple backends
|
8 |
|
9 | [![build](https://github.com/jaredwray/keyv/actions/workflows/build.yaml/badge.svg)](https://github.com/jaredwray/keyv/actions/workflows/build.yaml)
|
10 | [![codecov](https://codecov.io/gh/jaredwray/keyv/branch/master/graph/badge.svg?token=bRzR3RyOXZ)](https://codecov.io/gh/jaredwray/keyv)
|
11 | [![npm](https://img.shields.io/npm/dm/keyv.svg)](https://www.npmjs.com/package/keyv)
|
12 | [![npm](https://img.shields.io/npm/v/keyv.svg)](https://www.npmjs.com/package/keyv)
|
13 |
|
14 | Keyv provides a consistent interface for key-value storage across multiple backends via storage adapters. It supports TTL based expiry, making it suitable as a cache or a persistent key-value store.
|
15 |
|
16 | ## Features
|
17 |
|
18 | There are a few existing modules similar to Keyv, however Keyv is different because it:
|
19 |
|
20 | - Isn't bloated
|
21 | - Has a simple Promise based API
|
22 | - Suitable as a TTL based cache or persistent key-value store
|
23 | - [Easily embeddable](#add-cache-support-to-your-module) inside another module
|
24 | - Works with any storage that implements the [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) API
|
25 | - Handles all JSON types plus `Buffer`
|
26 | - Supports namespaces
|
27 | - Wide range of [**efficient, well tested**](#official-storage-adapters) storage adapters
|
28 | - Connection errors are passed through (db failures won't kill your app)
|
29 | - Supports the current active LTS version of Node.js or higher
|
30 |
|
31 | ## Usage
|
32 |
|
33 | Install Keyv.
|
34 |
|
35 | ```
|
36 | npm install --save keyv
|
37 | ```
|
38 |
|
39 | By default everything is stored in memory, you can optionally also install a storage adapter.
|
40 |
|
41 | ```
|
42 | npm install --save @keyv/redis
|
43 | npm install --save @keyv/mongo
|
44 | npm install --save @keyv/sqlite
|
45 | npm install --save @keyv/postgres
|
46 | npm install --save @keyv/mysql
|
47 | ```
|
48 |
|
49 | Create a new Keyv instance, passing your connection string if applicable. Keyv will automatically load the correct storage adapter.
|
50 |
|
51 | ```js
|
52 | const Keyv = require('keyv');
|
53 |
|
54 | // One of the following
|
55 | const keyv = new Keyv();
|
56 | const keyv = new Keyv('redis://user:pass@localhost:6379');
|
57 | const keyv = new Keyv('mongodb://user:pass@localhost:27017/dbname');
|
58 | const keyv = new Keyv('sqlite://path/to/database.sqlite');
|
59 | const keyv = new Keyv('postgresql://user:pass@localhost:5432/dbname');
|
60 | const keyv = new Keyv('mysql://user:pass@localhost:3306/dbname');
|
61 |
|
62 | // Handle DB connection errors
|
63 | keyv.on('error', err => console.log('Connection Error', err));
|
64 |
|
65 | await keyv.set('foo', 'expires in 1 second', 1000); // true
|
66 | await keyv.set('foo', 'never expires'); // true
|
67 | await keyv.get('foo'); // 'never expires'
|
68 | await keyv.delete('foo'); // true
|
69 | await keyv.clear(); // undefined
|
70 | ```
|
71 |
|
72 | ### Namespaces
|
73 |
|
74 | You can namespace your Keyv instance to avoid key collisions and allow you to clear only a certain namespace while using the same database.
|
75 |
|
76 | ```js
|
77 | const users = new Keyv('redis://user:pass@localhost:6379', { namespace: 'users' });
|
78 | const cache = new Keyv('redis://user:pass@localhost:6379', { namespace: 'cache' });
|
79 |
|
80 | await users.set('foo', 'users'); // true
|
81 | await cache.set('foo', 'cache'); // true
|
82 | await users.get('foo'); // 'users'
|
83 | await cache.get('foo'); // 'cache'
|
84 | await users.clear(); // undefined
|
85 | await users.get('foo'); // undefined
|
86 | await cache.get('foo'); // 'cache'
|
87 | ```
|
88 |
|
89 | ### Custom Serializers
|
90 |
|
91 | Keyv uses [`json-buffer`](https://github.com/dominictarr/json-buffer) for data serialization to ensure consistency across different backends.
|
92 |
|
93 | You can optionally provide your own serialization functions to support extra data types or to serialize to something other than JSON.
|
94 |
|
95 | ```js
|
96 | const keyv = new Keyv({ serialize: JSON.stringify, deserialize: JSON.parse });
|
97 | ```
|
98 |
|
99 | **Warning:** Using custom serializers means you lose any guarantee of data consistency. You should do extensive testing with your serialisation functions and chosen storage engine.
|
100 |
|
101 | ## Official Storage Adapters
|
102 |
|
103 | The official storage adapters are covered by [over 150 integration tests](https://github.com/jaredwray/keyv/actions/workflows/build.yaml) to guarantee consistent behaviour. They are lightweight, efficient wrappers over the DB clients making use of indexes and native TTLs where available.
|
104 |
|
105 | Database | Adapter | Native TTL | Status
|
106 | ---|---|---|---
|
107 | Redis | [@keyv/redis](https://github.com/lukechilds/keyv-redis) | Yes | [![build](https://github.com/lukechilds/keyv-redis/actions/workflows/build.yaml/badge.svg)](https://github.com/lukechilds/keyv-redis/actions/workflows/build.yaml) [![Coverage Status](https://coveralls.io/repos/github/lukechilds/keyv-redis/badge.svg?branch=master)](https://coveralls.io/github/lukechilds/keyv-redis?branch=master)
|
108 | MongoDB | [@keyv/mongo](https://github.com/lukechilds/keyv-mongo) | Yes | [![build](https://github.com/lukechilds/keyv-mongo/actions/workflows/build.yaml/badge.svg)](https://github.com/lukechilds/keyv-mongo/actions/workflows/build.yaml) [![Coverage Status](https://coveralls.io/repos/github/lukechilds/keyv-mongo/badge.svg?branch=master)](https://coveralls.io/github/lukechilds/keyv-mongo?branch=master)
|
109 | SQLite | [@keyv/sqlite](https://github.com/lukechilds/keyv-sqlite) | No | [![build](https://github.com/lukechilds/keyv-sqlite/actions/workflows/build.yaml/badge.svg)](https://github.com/lukechilds/keyv-sqlite/actions/workflows/build.yaml) [![Coverage Status](https://coveralls.io/repos/github/lukechilds/keyv-sqlite/badge.svg?branch=master)](https://coveralls.io/github/lukechilds/keyv-sqlite?branch=master)
|
110 | PostgreSQL | [@keyv/postgres](https://github.com/lukechilds/keyv-postgres) | No | [![build](https://github.com/lukechilds/keyv-postgres/actions/workflows/build.yaml/badge.svg)](https://github.com/lukechilds/keyv-postgres/actions/workflows/build.yaml) [![Coverage Status](https://coveralls.io/repos/github/lukechilds/keyv-postgres/badge.svg?branch=master)](https://coveralls.io/github/lukechilds/keyv-postgres?branch=master)
|
111 | MySQL | [@keyv/mysql](https://github.com/lukechilds/keyv-mysql) | No | [![build](https://github.com/jaredwray/keyv-mysql/actions/workflows/build.yaml/badge.svg)](https://github.com/jaredwray/keyv-mysql/actions/workflows/build.yaml) [![Coverage Status](https://coveralls.io/repos/github/lukechilds/keyv-mysql/badge.svg?branch=master)](https://coveralls.io/github/lukechilds/keyv-mysql?branch=master)
|
112 |
|
113 | ## Third-party Storage Adapters
|
114 |
|
115 | You can also use third-party storage adapters or build your own. Keyv will wrap these storage adapters in TTL functionality and handle complex types internally.
|
116 |
|
117 | ```js
|
118 | const Keyv = require('keyv');
|
119 | const myAdapter = require('./my-storage-adapter');
|
120 |
|
121 | const keyv = new Keyv({ store: myAdapter });
|
122 | ```
|
123 |
|
124 | Any store that follows the [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) api will work.
|
125 |
|
126 | ```js
|
127 | new Keyv({ store: new Map() });
|
128 | ```
|
129 |
|
130 | For example, [`quick-lru`](https://github.com/sindresorhus/quick-lru) is a completely unrelated module that implements the Map API.
|
131 |
|
132 | ```js
|
133 | const Keyv = require('keyv');
|
134 | const QuickLRU = require('quick-lru');
|
135 |
|
136 | const lru = new QuickLRU({ maxSize: 1000 });
|
137 | const keyv = new Keyv({ store: lru });
|
138 | ```
|
139 |
|
140 | The following are third-party storage adapters compatible with Keyv:
|
141 |
|
142 | - [quick-lru](https://github.com/sindresorhus/quick-lru) - Simple "Least Recently Used" (LRU) cache
|
143 | - [keyv-file](https://github.com/zaaack/keyv-file) - File system storage adapter for Keyv
|
144 | - [keyv-dynamodb](https://www.npmjs.com/package/keyv-dynamodb) - DynamoDB storage adapter for Keyv
|
145 | - [keyv-lru](https://www.npmjs.com/package/keyv-lru) - LRU storage adapter for Keyv
|
146 | - [keyv-null](https://www.npmjs.com/package/keyv-null) - Null storage adapter for Keyv
|
147 | - [keyv-firestore ](https://github.com/goto-bus-stop/keyv-firestore) – Firebase Cloud Firestore adapter for Keyv
|
148 | - [keyv-mssql](https://github.com/pmorgan3/keyv-mssql) - Microsoft Sql Server adapter for Keyv
|
149 | - [keyv-memcache](https://github.com/jaredwray/keyv-memcache) - Memcache storage adapter for Keyv
|
150 |
|
151 | ## Add Cache Support to your Module
|
152 |
|
153 | Keyv is designed to be easily embedded into other modules to add cache support. The recommended pattern is to expose a `cache` option in your modules options which is passed through to Keyv. Caching will work in memory by default and users have the option to also install a Keyv storage adapter and pass in a connection string, or any other storage that implements the `Map` API.
|
154 |
|
155 | You should also set a namespace for your module so you can safely call `.clear()` without clearing unrelated app data.
|
156 |
|
157 | Inside your module:
|
158 |
|
159 | ```js
|
160 | class AwesomeModule {
|
161 | constructor(opts) {
|
162 | this.cache = new Keyv({
|
163 | uri: typeof opts.cache === 'string' && opts.cache,
|
164 | store: typeof opts.cache !== 'string' && opts.cache,
|
165 | namespace: 'awesome-module'
|
166 | });
|
167 | }
|
168 | }
|
169 | ```
|
170 |
|
171 | Now it can be consumed like this:
|
172 |
|
173 | ```js
|
174 | const AwesomeModule = require('awesome-module');
|
175 |
|
176 | // Caches stuff in memory by default
|
177 | const awesomeModule = new AwesomeModule();
|
178 |
|
179 | // After npm install --save keyv-redis
|
180 | const awesomeModule = new AwesomeModule({ cache: 'redis://localhost' });
|
181 |
|
182 | // Some third-party module that implements the Map API
|
183 | const awesomeModule = new AwesomeModule({ cache: some3rdPartyStore });
|
184 | ```
|
185 |
|
186 | ## API
|
187 |
|
188 | ### new Keyv([uri], [options])
|
189 |
|
190 | Returns a new Keyv instance.
|
191 |
|
192 | The Keyv instance is also an `EventEmitter` that will emit an `'error'` event if the storage adapter connection fails.
|
193 |
|
194 | ### uri
|
195 |
|
196 | Type: `String`<br>
|
197 | Default: `undefined`
|
198 |
|
199 | The connection string URI.
|
200 |
|
201 | Merged into the options object as options.uri.
|
202 |
|
203 | ### options
|
204 |
|
205 | Type: `Object`
|
206 |
|
207 | The options object is also passed through to the storage adapter. Check your storage adapter docs for any extra options.
|
208 |
|
209 | #### options.namespace
|
210 |
|
211 | Type: `String`<br>
|
212 | Default: `'keyv'`
|
213 |
|
214 | Namespace for the current instance.
|
215 |
|
216 | #### options.ttl
|
217 |
|
218 | Type: `Number`<br>
|
219 | Default: `undefined`
|
220 |
|
221 | Default TTL. Can be overridden by specififying a TTL on `.set()`.
|
222 |
|
223 | #### options.serialize
|
224 |
|
225 | Type: `Function`<br>
|
226 | Default: `JSONB.stringify`
|
227 |
|
228 | A custom serialization function.
|
229 |
|
230 | #### options.deserialize
|
231 |
|
232 | Type: `Function`<br>
|
233 | Default: `JSONB.parse`
|
234 |
|
235 | A custom deserialization function.
|
236 |
|
237 | #### options.store
|
238 |
|
239 | Type: `Storage adapter instance`<br>
|
240 | Default: `new Map()`
|
241 |
|
242 | The storage adapter instance to be used by Keyv.
|
243 |
|
244 | #### options.adapter
|
245 |
|
246 | Type: `String`<br>
|
247 | Default: `undefined`
|
248 |
|
249 | Specify an adapter to use. e.g `'redis'` or `'mongodb'`.
|
250 |
|
251 | ### Instance
|
252 |
|
253 | Keys must always be strings. Values can be of any type.
|
254 |
|
255 | #### .set(key, value, [ttl])
|
256 |
|
257 | Set a value.
|
258 |
|
259 | By default keys are persistent. You can set an expiry TTL in milliseconds.
|
260 |
|
261 | Returns a promise which resolves to `true`.
|
262 |
|
263 | #### .get(key, [options])
|
264 |
|
265 | Returns a promise which resolves to the retrieved value.
|
266 |
|
267 | ##### options.raw
|
268 |
|
269 | Type: `Boolean`<br>
|
270 | Default: `false`
|
271 |
|
272 | If set to true the raw DB object Keyv stores internally will be returned instead of just the value.
|
273 |
|
274 | This contains the TTL timestamp.
|
275 |
|
276 | #### .delete(key)
|
277 |
|
278 | Deletes an entry.
|
279 |
|
280 | Returns a promise which resolves to `true` if the key existed, `false` if not.
|
281 |
|
282 | #### .clear()
|
283 |
|
284 | Delete all entries in the current namespace.
|
285 |
|
286 | Returns a promise which is resolved when the entries have been cleared.
|
287 |
|
288 | ## License
|
289 |
|
290 | MIT © Jared Wray & Luke Childs
|