@keyv/mongo
v6.1.0
Published
MongoDB storage adapter for Keyv
Maintainers
Readme
@keyv/mongo
MongoDB storage adapter for Keyv
MongoDB storage adapter for Keyv.
Uses TTL indexes to automatically remove expired documents. However MongoDB doesn't guarantee data will be deleted immediately upon expiration, so expiry dates are revalidated in Keyv.
Table of Contents
Install
npm install --save keyv @keyv/mongoUsage
import Keyv from 'keyv';
import KeyvMongo from '@keyv/mongo';
const keyv = new Keyv(new KeyvMongo('mongodb://user:pass@localhost:27017/dbname'));
keyv.on('error', handleConnectionError);You can specify the collection name, by default 'keyv' is used.
e.g:
const keyv = new Keyv(new KeyvMongo('mongodb://user:pass@localhost:27017/dbname', { collection: 'cache' }));You can also use the createKeyv helper function to create a Keyv instance with KeyvMongo as the store:
import { createKeyv } from '@keyv/mongo';
const keyv = createKeyv('mongodb://user:pass@localhost:27017/dbname');Constructor Options
The KeyvMongo constructor accepts a connection URI string or an options object:
// With URI string
const store = new KeyvMongo('mongodb://user:pass@localhost:27017/dbname');
// With options object
const store = new KeyvMongo({
url: 'mongodb://user:pass@localhost:27017/dbname',
collection: 'cache',
db: 'mydb',
useGridFS: false,
});
// With URI string and additional options
const store = new KeyvMongo('mongodb://user:pass@localhost:27017/dbname', { collection: 'cache' });| Option | Type | Default | Description |
|---|---|---|---|
| url | string | 'mongodb://127.0.0.1:27017' | MongoDB connection URI |
| collection | string | 'keyv' | Collection name for storage |
| namespace | string \| undefined | undefined | Namespace for keys, stored in each document's namespace field |
| useGridFS | boolean | false | Whether to use GridFS for storing values |
| db | string \| undefined | undefined | Database name |
| readPreference | ReadPreference \| undefined | undefined | MongoDB read preference for GridFS operations |
Any additional options are passed through to the MongoDB driver as MongoClientOptions.
Properties
Most configuration options are exposed as properties with getters and setters on the KeyvMongo instance. You can read or update them after construction. Some properties like useGridFS are read-only and can only be set via the constructor.
url
Get or set the MongoDB connection URI.
- Type:
string - Default:
'mongodb://127.0.0.1:27017'
const store = new KeyvMongo({ url: 'mongodb://user:pass@localhost:27017/dbname' });
console.log(store.url); // 'mongodb://user:pass@localhost:27017/dbname'collection
Get or set the collection name used for storage.
- Type:
string - Default:
'keyv'
const store = new KeyvMongo({ url: 'mongodb://user:pass@localhost:27017/dbname' });
console.log(store.collection); // 'keyv'
store.collection = 'cache';namespace
Get or set the namespace for the adapter. It is stored in each document's namespace field (or metadata.namespace with GridFS) and scopes operations like clear().
- Type:
string | undefined - Default:
undefined
const store = new KeyvMongo({ url: 'mongodb://user:pass@localhost:27017/dbname' });
store.namespace = 'my-namespace';
console.log(store.namespace); // 'my-namespace'useGridFS
Get whether GridFS is used for storing values. When enabled, values are stored using MongoDB's GridFS specification, which is useful for storing large files. This property is read-only and can only be set via the constructor, because the connection shape differs between GridFS and standard modes.
Each value is a GridFS file named after its key. set writes a new file and then deletes the key's older files, so a key keeps a single file. Reads always use the newest file for a key, and delete removes every file for it.
- Type:
boolean - Default:
false - Read-only (set via constructor only)
const store = new KeyvMongo({ url: 'mongodb://user:pass@localhost:27017/dbname', useGridFS: true });
console.log(store.useGridFS); // truedb
Get or set the database name for the MongoDB connection.
- Type:
string | undefined - Default:
undefined
const store = new KeyvMongo({ url: 'mongodb://user:pass@localhost:27017', db: 'mydb' });
console.log(store.db); // 'mydb'readPreference
Get or set the MongoDB read preference for GridFS operations.
- Type:
ReadPreference | undefined - Default:
undefined
import { ReadPreference } from 'mongodb';
const store = new KeyvMongo({
url: 'mongodb://user:pass@localhost:27017/dbname',
useGridFS: true,
readPreference: ReadPreference.SECONDARY,
});
console.log(store.readPreference); // ReadPreference.SECONDARYMethods
set
set(key, value, expires?) - Set a value in the store.
key(string) - The key to set.value(any) - The value to store.expires(number, optional) - Absolute expiry time as a Unix timestamp in milliseconds (Date.now() + ttl). If specified, the key expires at this time.- Returns:
Promise<boolean>
Through a Keyv instance, keyv.set(key, value, ttl) still takes a relative ttl in milliseconds. Keyv converts it to expires before it calls the adapter.
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar');
await keyv.set('foo', 'bar', 5000); // Keyv takes a relative ttl: expires in 5 seconds
await store.set('foo', 'bar', Date.now() + 5000); // the adapter takes an absolute expiressetMany
setMany(entries) - Set multiple values in the store at once.
entries(KeyvStorageEntry[]) - Array of entries to set. Each entry has akey,value, and optionalexpires(an absolute Unix timestamp in milliseconds). Attlfield is ignored.Valueis inferred from the entries provided.- Returns:
Promise<boolean[]>- An array of booleans indicating whether each entry was set successfully.
Through a Keyv instance, keyv.setMany() takes entries with a relative ttl ({ key, value, ttl? }) and converts them before it calls the adapter.
In standard mode, uses a single unordered MongoDB bulkWrite operation for efficiency with per-entry error tracking — if individual writes fail, only those entries return false. In GridFS mode, each entry is set individually in parallel using Promise.allSettled, providing per-entry success tracking.
const store = new KeyvMongo('mongodb://localhost:27017');
const results = await store.setMany([
{ key: 'key1', value: 'value1' },
{ key: 'key2', value: 'value2', expires: Date.now() + 5000 },
]); // [true, true]get
get(key) - Get a value from the store.
key(string) - The key to retrieve.- Returns:
Promise<any>- The stored value, orundefinedif the key does not exist.
In GridFS mode, get also updates the lastAccessed timestamp on the file, which is used by clearUnusedFor.
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar');
const value = await keyv.get('foo');
console.log(value); // 'bar'
const missing = await keyv.get('nonexistent');
console.log(missing); // undefinedgetMany
getMany(keys) - Get multiple values from the store at once.
keys(string[]) - Array of keys to retrieve.- Returns:
Promise<Array<any>>- Array of values in the same order as the input keys. Missing keys returnundefinedat their position.
In standard mode, uses a single MongoDB query with the $in operator for efficiency. In GridFS mode, each key is fetched individually.
const store = new KeyvMongo('mongodb://localhost:27017');
await store.set('key1', 'value1');
await store.set('key2', 'value2');
const values = await store.getMany(['key1', 'key2', 'key3']);
console.log(values); // ['value1', 'value2', undefined]has
has(key) - Check if a key exists in the store.
key(string) - The key to check.- Returns:
Promise<boolean>-trueif the key exists,falseotherwise.
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar');
console.log(await keyv.has('foo')); // true
console.log(await keyv.has('nonexistent')); // falsehasMany
hasMany(keys) - Check if multiple keys exist in the store at once.
keys(string[]) - Array of keys to check.- Returns:
Promise<boolean[]>- Array of booleans in the same order as the input keys.
Uses a single MongoDB query with the $in operator for efficiency in both standard and GridFS modes.
const store = new KeyvMongo('mongodb://localhost:27017');
await store.set('key1', 'value1');
await store.set('key2', 'value2');
const results = await store.hasMany(['key1', 'key2', 'key3']);
console.log(results); // [true, true, false]delete
delete(key) - Delete a key from the store.
key(string) - The key to delete.- Returns:
Promise<boolean>-trueif the key was deleted,falseif the key was not found.
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store });
await keyv.set('foo', 'bar');
console.log(await keyv.delete('foo')); // true
console.log(await keyv.delete('nonexistent')); // falsedeleteMany
deleteMany(keys) - Delete multiple keys from the store at once.
keys(string[]) - Array of keys to delete.- Returns:
Promise<boolean[]>- An array of booleans indicating whether each key was deleted.
Each key is deleted individually in parallel so that per-key success/failure information is preserved in the returned array.
const store = new KeyvMongo('mongodb://localhost:27017');
await store.set('key1', 'value1');
await store.set('key2', 'value2');
const results = await store.deleteMany(['key1', 'key2', 'key3']); // [true, true, false]clear
clear() - Delete all keys in the current namespace.
- Returns:
Promise<void>
Only keys matching the current namespace are removed. If no namespace is set, all keys with an empty namespace are cleared.
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store, namespace: 'my-namespace' });
await keyv.set('key1', 'value1');
await keyv.set('key2', 'value2');
await keyv.clear(); // removes all keys in 'my-namespace'iterator
iterator() - Iterate over all key-value pairs in the store. The iterator uses the namespace configured on the instance.
- Returns:
AsyncGenerator<[string, any]>- An async generator yielding[key, value]pairs.
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store, namespace: 'ns' });
await keyv.set('key1', 'value1');
await keyv.set('key2', 'value2');
for await (const [key, value] of keyv.iterator()) {
console.log(key, value); // 'key1' 'value1', 'key2' 'value2'
}disconnect
disconnect() - Close the MongoDB connection.
- Returns:
Promise<void>
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store });
// ... use the store ...
await keyv.disconnect();clearExpired
clearExpired() - Remove all expired files from GridFS. This method only works in GridFS mode and is a no-op that returns false in standard mode.
- Returns:
Promise<boolean>-trueif running in GridFS mode,falseotherwise.
This is useful for manual cleanup of expired GridFS files, since GridFS does not support MongoDB TTL indexes.
const store = new KeyvMongo({ url: 'mongodb://localhost:27017', useGridFS: true });
await store.set('temp', 'data', Date.now() + 1000); // adapter takes an absolute expires (Unix ms); expires in ~1 second
// After expiration...
await store.clearExpired(); // removes expired GridFS filesclearUnusedFor
clearUnusedFor(seconds) - Remove all GridFS files that have not been accessed for the specified duration. This method only works in GridFS mode and is a no-op that returns false in standard mode.
seconds(number) - The number of seconds of inactivity after which files should be removed.- Returns:
Promise<boolean>-trueif running in GridFS mode,falseotherwise.
The lastAccessed timestamp is updated each time a file is read via get.
const store = new KeyvMongo({ url: 'mongodb://localhost:27017', useGridFS: true });
await store.set('foo', 'bar');
// Remove files not accessed in the last hour
await store.clearUnusedFor(3600);Migration from v3 to v6
@keyv/mongo is jumping from v3 to v6 because the entire Keyv monorepo is now unified under a single version number. This approach, similar to what other popular open source projects do, keeps all @keyv/* packages in sync and makes it easier to reason about compatibility across the ecosystem.
Breaking Changes
MongoDB Driver Upgraded to v7
The mongodb dependency has been upgraded from ^6.x to ^7.0.0. Review the MongoDB Node.js Driver v7 release notes for any breaking changes that may affect your application.
Event Handling: EventEmitter Replaced with Hookified
KeyvMongo no longer extends Node.js EventEmitter. It now extends Hookified, which provides hook-based event management.
// v3 - EventEmitter
const store = new KeyvMongo('mongodb://localhost:27017');
store.on('error', err => console.error(err));
// v6 - Hookified (same usage for basic events)
const store = new KeyvMongo('mongodb://localhost:27017');
store.on('error', err => console.error(err));For most use cases the .on() API is the same, but if you relied on EventEmitter-specific methods like .listenerCount(), .rawListeners(), or .prependListener(), check the Hookified documentation for equivalents.
useGridFS is Now Read-Only
The useGridFS property can no longer be changed after construction. The connection shape differs between GridFS and standard modes, so this must be set at construction time.
// v3 - Could change after instantiation
const store = new KeyvMongo('mongodb://localhost:27017');
store.useGridFS = true; // worked in v3
// v6 - Must set at construction
const store = new KeyvMongo({ url: 'mongodb://localhost:27017', useGridFS: true });
console.log(store.useGridFS); // true (read-only)ttlSupport Property Removed
The ttlSupport property has been removed. If your code checks for TTL support on the adapter, remove those checks.
set Takes an Absolute expires
The third argument of the adapter's set is now an absolute Unix timestamp in milliseconds, not a relative ttl. This only affects code that calls the adapter directly. keyv.set(key, value, ttl) still takes a relative ttl.
// v3 - relative ttl
await store.set('foo', 'bar', 5000);
// v6 - absolute expires
await store.set('foo', 'bar', Date.now() + 5000);Namespaces Are Stored in Their Own Field
v3 stored the namespace as a prefix of each key (key: "keyv:foo") and wrote no namespace field. v6 stores it separately (key: "foo", namespace: "keyv") and only reads documents that have a namespace field, so documents written by v3 stay invisible to v6 until you run the migration script.
The unique index on the underlying MongoDB collection has also changed from { key: 1 } to { key: 1, namespace: 1 }, so the same key can exist in different namespaces. The adapter drops the old index and creates the new one when it connects.
Options Type is Now Strongly Typed
The constructor options no longer accept arbitrary keys via [key: string]: unknown. Options are now strictly typed with explicit properties. Any additional MongoDB driver options should be valid MongoClientOptions properties.
// v3 - Accepted any properties
const store = new KeyvMongo({ url: 'mongodb://...', customProp: true }); // no type error
// v6 - Strictly typed
const store = new KeyvMongo({ url: 'mongodb://...', collection: 'cache' }); // only known props + MongoClientOptionsNew Features
createKeyv Helper Function
A new createKeyv helper simplifies creating a Keyv instance with the MongoDB adapter.
import { createKeyv } from '@keyv/mongo';
// Before
const store = new KeyvMongo('mongodb://localhost:27017');
const keyv = new Keyv({ store, namespace: 'my-ns' });
// After
const keyv = createKeyv({ url: 'mongodb://localhost:27017', namespace: 'my-ns' });setMany Method
Batch set multiple key-value pairs in a single operation using MongoDB bulkWrite. Each entry takes an optional absolute expires timestamp (Unix ms).
await store.setMany([
{ key: 'key1', value: 'value1' },
{ key: 'key2', value: 'value2', expires: Date.now() + 5000 },
]);hasMany Method
Check if multiple keys exist in a single query using the $in operator.
const results = await store.hasMany(['key1', 'key2', 'key3']);
// [true, true, false]clearExpired Method
Manually remove expired files from GridFS storage.
const store = new KeyvMongo({ url: 'mongodb://localhost:27017', useGridFS: true });
await store.clearExpired();clearUnusedFor Method
Remove GridFS files that have not been accessed for a specified duration.
const store = new KeyvMongo({ url: 'mongodb://localhost:27017', useGridFS: true });
await store.clearUnusedFor(3600); // remove files unused for 1 hourRunning the migration script
If you have documents written by @keyv/mongo v3, run the migration script so v6 can read them. The script ships in the npm package at scripts/migrate-v6.ts. Run it from your project root with tsx. Node.js refuses to strip TypeScript types from files under node_modules, so running the script with plain node from there fails with ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING.
Preview the changes first with --dry-run. Dry-run mode only counts and previews the documents that would change:
npx tsx node_modules/@keyv/mongo/scripts/migrate-v6.ts --uri mongodb://user:pass@localhost:27017/dbname --dry-runRun the migration:
npx tsx node_modules/@keyv/mongo/scripts/migrate-v6.ts --uri mongodb://user:pass@localhost:27017/dbnamePass --collection if you set a custom collection, --db if the database is not in the URI, and --gridfs for a store created with useGridFS: true:
npx tsx node_modules/@keyv/mongo/scripts/migrate-v6.ts --uri mongodb://user:pass@localhost:27017 --db cache --collection sessions
npx tsx node_modules/@keyv/mongo/scripts/migrate-v6.ts --uri mongodb://user:pass@localhost:27017/dbname --gridfsFrom a clone of this repo you can run node scripts/migrate-v6.ts in storage/mongo with the same flags.
Important notes:
- The script only changes documents that have no
namespacefield, so it is safe to run again. Documents already in the v6 layout are never touched. - Keys are split on the first colon: the part before becomes the namespace, the rest becomes the key. A key without a colon gets the empty namespace v6 uses when none is set. Namespaces containing colons are not supported. The split is also wrong for colon-containing keys that Keyv v5 stored without a prefix, which happens only when it ran with
useKeyPrefix: falseor an empty namespace. - Keyv v5 used
keyvas the namespace when none was set, so those documents migrate into namespacekeyv. Passnamespace: 'keyv'to Keyv afterwards to keep reading them. See the v5 to v6 migration guide. - In standard mode the script first replaces the unique index on
keywith the unique index on{ key, namespace }, the same change the adapter makes when it connects. - If v6 already stores a value under the same key and namespace, that value was written after the upgrade and is kept. The v3 document is left unchanged and listed in the output.
- In GridFS mode, v3 kept every revision of a file and read the newest. The script migrates only the newest revision of each key and lists the older ones, which v6 cannot read.
- The rewrite is not transactional. If it stops part way, documents already migrated keep their new layout, so run the script again to finish.
