diff --git a/packages/cache-manager/README.md b/packages/cache-manager/README.md index 09c8b3f4..a9d36646 100644 --- a/packages/cache-manager/README.md +++ b/packages/cache-manager/README.md @@ -39,11 +39,16 @@ If you are looking for older documentation you can find it here: * [.mdel](#mdel) * [.clear](#clear) * [.wrap](#wrap) + * [.disconnect](#disconnect) * [Events](#events) * [.set](#set) * [.del](#del) * [.clear](#clear) * [.refresh](#refresh) +* [Properties](#properties) + * [.cacheId](#cacheId) + * [.stores](#stores) +* [Doing Iteration on Stores](#doing-iteration-on-stores) * [Update on `redis` and `ioredis` Support](#update-on-redis-and-ioredis-support) * [Using Legacy Storage Adapters](#using-legacy-storage-adapters) * [Contribute](#contribute) @@ -370,10 +375,17 @@ await cache.disconnect(); See unit tests in [`test/disconnect.test.ts`](./test/disconnect.test.ts) for more information. +# Properties + ## cacheId `cacheId(): string` -Returns cache instance id. +Returns cache instance id. This is primarily used to not have conflicts when using `wrap` with multiple cache instances. + +## stores +`stores(): Keyv[]` + +Returns the list of Keyv instances. This can be used to get the list of stores and then use the Keyv API to interact with the store directly. ```ts const cache = createCache({cacheId: 'my-cache-id'}); @@ -424,6 +436,38 @@ cache.on('refresh', ({ key, value, error }) => { See unit tests in [`test/events.test.ts`](./test/events.test.ts) for more information. +# Doing Iteration on Stores + +You can use the `stores` method to get the list of stores and then use the Keyv API to interact with the store directly. Below is an example of iterating over all stores and getting all keys: + +```ts +import Keyv from 'keyv'; +import { createKeyv } from '@keyv/redis'; +import { createCache } from 'cache-manager'; + +const keyv = new Keyv(); +const keyvRedis = createKeyv('redis://user:pass@localhost:6379'); + +const cache = createCache({ + stores: [keyv, keyvRedis], +}); + +// add some data +await cache.set('key-1', 'value 1'); +await cache.set('key-2', 'value 2'); + +// get the store you want to iterate over. In this example we are using the second store (redis) +const store = cache.stores[1]; + +if(store?.iterator) { + for await (const [key, value] of store.iterator({})) { + console.log(key, value); + } +} +``` + +WARNING: Be careful when using `iterator` as it can cause major performance issues with the amount of data being retrieved. Also, Not all storage adapters support `iterator` so you may need to check the documentation for the storage adapter you are using. + # Update on redis and ioredis Support We will not be supporting `cache-manager-ioredis-yet` or `cache-manager-redis-yet` in the future as we have moved to using `Keyv` as the storage adapter `@keyv/redis`. diff --git a/packages/cache-manager/package.json b/packages/cache-manager/package.json index 0f8e2f9a..f5af161f 100644 --- a/packages/cache-manager/package.json +++ b/packages/cache-manager/package.json @@ -1,6 +1,6 @@ { "name": "cache-manager", - "version": "6.3.2", + "version": "6.4.0", "description": "Cache Manager for Node.js", "type": "module", "main": "./dist/index.cjs", @@ -60,16 +60,16 @@ "keyv": "^5.2.3" }, "devDependencies": { - "@faker-js/faker": "^9.3.0", + "@faker-js/faker": "^9.4.0", "@keyv/redis": "^4.2.0", - "@types/node": "^22.10.2", - "@vitest/coverage-v8": "^2.1.8", + "@types/node": "^22.10.9", + "@vitest/coverage-v8": "^3.0.4", "cache-manager-redis-yet": "^5.1.5", "cacheable": "workspace:^", "rimraf": "^6.0.1", "tsup": "^8.3.5", - "typescript": "^5.7.2", - "vitest": "^2.1.8", + "typescript": "^5.7.3", + "vitest": "^3.0.4", "xo": "^0.60.0" }, "xo": { diff --git a/packages/cache-manager/src/index.ts b/packages/cache-manager/src/index.ts index 7cb1053a..9953b6c7 100644 --- a/packages/cache-manager/src/index.ts +++ b/packages/cache-manager/src/index.ts @@ -53,6 +53,7 @@ export type Cache = { ) => EventEmitter; disconnect: () => Promise; cacheId: () => string; + stores: Keyv[]; }; export type Events = { @@ -334,6 +335,7 @@ export const createCache = (options?: CreateCacheOptions): Cache => { off, disconnect, cacheId, + stores, }; }; diff --git a/packages/cache-manager/test/stores.test.ts b/packages/cache-manager/test/stores.test.ts new file mode 100644 index 00000000..f1f6ec0e --- /dev/null +++ b/packages/cache-manager/test/stores.test.ts @@ -0,0 +1,45 @@ +import {Keyv} from 'keyv'; +import {createKeyv} from '@keyv/redis'; +import { + describe, expect, it, +} from 'vitest'; +import {simpleFaker} from '@faker-js/faker'; +import {createCache} from '../src/index.js'; + +describe('stores', () => { + it('can get the keyv store', () => { + const cache = createCache(); + expect(cache.stores.length).toEqual(1); + }); + + it('can see multiple stores', () => { + const keyv = new Keyv(); + const redis = createKeyv(); + const cache = createCache({stores: [keyv, redis]}); + expect(cache.stores.length).toEqual(2); + expect(cache.stores[0]).toEqual(keyv); + expect(cache.stores[1]).toEqual(redis); + }); + + it('can get the keyv store and do iterator', async () => { + const cache = createCache(); + expect(cache.stores.length).toEqual(1); + const keyName = simpleFaker.string.uuid(); + const keyValue = simpleFaker.string.uuid(); + await cache.set(keyName, keyValue); + const keyv = cache.stores[0]; + expect(keyv).toBeInstanceOf(Keyv); + + let returnValue; + + if (keyv?.iterator) { + for await (const [key, value] of keyv.iterator({})) { + if (key === keyName) { + returnValue = value; + } + } + } + + expect(returnValue).toEqual(keyValue); + }); +});