@seedcompany/cache
v2.0.1
Published
NestJS library wrapper for external caching
Downloads
333
Readme
Yet another cache library for NestJS
This is for data that will be shared across multiple processes. Thus, the keys are strings and the values are serializable.
The goals of this library are:
- Provide a nice interface for usage in Nestjs classes
- Integrate easily with the existing Nestjs ecosystem
- Leverage existing npm ecosystem for storage implementations
Setup
Import the module in your app.
CacheModule.registerAsync({
inject: [/* config */],
useFactory: (/* config */): CacheModuleOptions => ({
store: // create store implementation from config
}),
})
Usage
Decorators
@Injectable()
class Service {
@Cache({
key: 'foo-bar',
ttl: { minutes: 5 },
})
async fooBar() {
// ...
}
}
However, you will probably need to cache based on the args given, so:
@Injectable()
class Service {
@Cache((id: string, year: number) => ({
key: `foo-bar:${id}-${year}`,
ttl: { minutes: 5 },
}))
async fooBar(id: string, year: number) {
// ...
}
}
Service
The task you always most always want to do is skip expensive calculation if possible.
So the most useful method is getOrCalculate
, which saves you the boilerplate for this logic.
declare const cache: CacheService; // injected somewhere
const result = await cache.getOrCalculate({
key: `fooBar:${id}`,
calculate: async () => {
// do expensive calculation
},
ttl: { hour: 1 },
});
Naive functionality exists as well:
await cache.get('foo');
await cache.set('foo', 'bar');
await cache.delete('foo');
Namespaces
A common use case is to namespace your cache keys, to help segment your cache store.
There's a namespace
method which returns a modified cache service with the given prefix.
const usersCache = cache.namespace('users:');
Note that chaining these will stack the prefixes, not override them.
cache.namespace('foo:').namespace('bar:').get('baz'); // foo:bar:baz
CacheItem
A wrapper is provided around a single cache keys as well. This could help prevent having to store another variable for a computed key, or allow it to be passed around.
const cached = cache.item('1234');
const value = cached.getOrCalculate(async () => {
// do expensive calculation
});
handleInvalidation(cached);
function handleInvalidation(cached: CacheItem) {
if (isStale) {
cached.delete();
}
}
Stores
Stores are the actual storage implementations the CacheService
is backed by.
We provide stores for ioredis
, keyv
, lru-cache
.
This should cover most use cases, but you can also implement your own.
LRU / In Memory
import { LruStore } from '@seedcompany/cache/store/lru';
CacheModule.register({
store: new LruStore({
// options
})
})
Redis
import { RedisStore } from '@seedcompany/cache/store/redis';
import Redis from 'ioredis';
CacheModule.register({
store: new RedisStore(
new Redis('redis://localhost:6379')
)
})
Keyv
import { KeyvStore } from '@seedcompany/cache/store/keyv';
import Keyv from 'keyv';
CacheModule.register({
store: new KeyvStore(
new Keyv()
)
})
Chaining Multiple Stores
Multiple stores can be chained together with the ChainStore
.
This could provide faster in memory cache falling back to a shared network cache.
import { ChainStore } from '@seedcompany/cache/store/chain';
CacheModule.register({
store: new ChainStore([
new LruStore({ max: 100 }),
new RedisStore(),
])
})
Adapters
We provide a few adapters to help integrate with other libraries.
NestJS Cache Manager
Here's how to set up the NestJS Cache Manager module to be based on this one.
import { CacheModule as NestHttpCacheModule } from '@nestjs/cache-manager';
import { CacheModule, CacheService } from '@seedcompany/cache';
NestHttpCacheModule.registerAsync({
imports: [CacheModule],
inject: [CacheService],
useFactory: (cache: CacheService): CacheModuleOptions => ({
store: cache.namespace('http:').adaptTo.cacheManager(),
}),
});
Continue on with the NestJS docs: https://docs.nestjs.com/techniques/caching#interacting-with-the-cache-store
Apollo Cache
Here's how to set up the Apollo cache to be based on this one.
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
imports: [CacheModule],
inject: [CacheService],
useFactory: (cache: CacheService) => ({
cache: cache.namespace('apollo:').adaptTo.apollo(),
}),
});
Keyv Cache
Keyv cache is a very popular cache library. Here's how to adapt our cache to its interface.
declare const cache: CacheService;
const kv = new Keyv({
store: cache.adaptTo.keyv(),
});